Skip to content
This repository was archived by the owner on Jun 14, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 3 additions & 84 deletions cmd/ci-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"encoding/base32"
"encoding/json"
"encoding/xml"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
Expand Down Expand Up @@ -426,7 +424,7 @@ func (o *options) Run() error {
}

if o.print {
if err := printDigraph(os.Stdout, buildSteps); err != nil {
if err := api.PrintDigraph(os.Stdout, buildSteps); err != nil {
return fmt.Errorf("could not print graph: %v", err)
}
return nil
Expand Down Expand Up @@ -977,91 +975,12 @@ func jobSpecFromGitRef(ref string) (*api.JobSpec, error) {
return &api.JobSpec{Type: api.PeriodicJob, Job: "dev", Refs: &api.Refs{Org: prefix[0], Repo: prefix[1], BaseRef: parts[1], BaseSHA: sha}}, nil
}

func nodeNames(nodes []*api.StepNode) []string {
var names []string
for _, node := range nodes {
name := node.Step.Name()
if len(name) == 0 {
name = fmt.Sprintf("<%T>", node.Step)
}
names = append(names, name)
}
return names
}

func linkNames(links []api.StepLink) []string {
var names []string
for _, link := range links {
name := fmt.Sprintf("<%#v>", link)
names = append(names, name)
}
return names
}

func topologicalSort(nodes []*api.StepNode) ([]*api.StepNode, error) {
var sortedNodes []*api.StepNode
var satisfied []api.StepLink
seen := make(map[api.Step]struct{})
for len(nodes) > 0 {
var changed bool
var waiting []*api.StepNode
for _, node := range nodes {
for _, child := range node.Children {
if _, ok := seen[child.Step]; !ok {
waiting = append(waiting, child)
}
}
if _, ok := seen[node.Step]; ok {
continue
}
if !api.HasAllLinks(node.Step.Requires(), satisfied) {
waiting = append(waiting, node)
continue
}
satisfied = append(satisfied, node.Step.Creates()...)
sortedNodes = append(sortedNodes, node)
seen[node.Step] = struct{}{}
changed = true
}
if !changed && len(waiting) > 0 {
for _, node := range waiting {
var missing []api.StepLink
for _, link := range node.Step.Requires() {
if !api.HasAllLinks([]api.StepLink{link}, satisfied) {
missing = append(missing, link)
}
log.Printf("step <%T> is missing dependencies: %s", node.Step, strings.Join(linkNames(missing), ", "))
}
}
return nil, errors.New("steps are missing dependencies")
}
nodes = waiting
}
return sortedNodes, nil
}

func printDigraph(w io.Writer, steps []api.Step) error {
for _, step := range steps {
for _, other := range steps {
if step == other {
continue
}
if api.HasAnyLinks(step.Requires(), other.Creates()) {
if _, err := fmt.Fprintf(w, "%s %s\n", step.Name(), other.Name()); err != nil {
return err
}
}
}
}
return nil
}

func printExecutionOrder(nodes []*api.StepNode) error {
ordered, err := topologicalSort(nodes)
ordered, err := api.TopologicalSort(nodes)
if err != nil {
return fmt.Errorf("could not sort nodes: %v", err)
}
log.Printf("Running %s", strings.Join(nodeNames(ordered), ", "))
log.Printf("Running %s", strings.Join(api.NodeNames(ordered), ", "))
return nil
}

Expand Down
88 changes: 88 additions & 0 deletions pkg/api/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package api

import (
"errors"
"fmt"
"io"
"log"
"strings"
)

func NodeNames(nodes []*StepNode) []string {
var names []string
for _, node := range nodes {
name := node.Step.Name()
if len(name) == 0 {
name = fmt.Sprintf("<%T>", node.Step)
}
names = append(names, name)
}
return names
}

func LinkNames(links []StepLink) []string {
var names []string
for _, link := range links {
name := fmt.Sprintf("<%#v>", link)
names = append(names, name)
}
return names
}

func TopologicalSort(nodes []*StepNode) ([]*StepNode, error) {
var sortedNodes []*StepNode
var satisfied []StepLink
seen := make(map[Step]struct{})
for len(nodes) > 0 {
var changed bool
var waiting []*StepNode
for _, node := range nodes {
for _, child := range node.Children {
if _, ok := seen[child.Step]; !ok {
waiting = append(waiting, child)
}
}
if _, ok := seen[node.Step]; ok {
continue
}
if !HasAllLinks(node.Step.Requires(), satisfied) {
waiting = append(waiting, node)
continue
}
satisfied = append(satisfied, node.Step.Creates()...)
sortedNodes = append(sortedNodes, node)
seen[node.Step] = struct{}{}
changed = true
}
if !changed && len(waiting) > 0 {
for _, node := range waiting {
var missing []StepLink
for _, link := range node.Step.Requires() {
if !HasAllLinks([]StepLink{link}, satisfied) {
missing = append(missing, link)
}
log.Printf("step <%T> is missing dependencies: %s", node.Step, strings.Join(LinkNames(missing), ", "))
}
}
return nil, errors.New("steps are missing dependencies")
}
nodes = waiting
}
return sortedNodes, nil
}

func PrintDigraph(w io.Writer, steps []Step) error {
for _, step := range steps {
for _, other := range steps {
if step == other {
continue
}
if HasAnyLinks(step.Requires(), other.Creates()) {
if _, err := fmt.Fprintf(w, "%s %s\n", step.Name(), other.Name()); err != nil {
return err
}
}
}
}
return nil
}
20 changes: 18 additions & 2 deletions pkg/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"net/url"
"strings"

"k8s.io/apimachinery/pkg/util/sets"

"github.com/openshift/ci-operator/pkg/steps/clusterinstall"

appsclientset "k8s.io/client-go/kubernetes/typed/apps/v1"
Expand Down Expand Up @@ -41,9 +43,17 @@ func FromConfig(
var buildSteps []api.Step
var postSteps []api.Step

requiredNames := make(map[string]struct{})
requiredNames := sets.NewString()
for _, target := range requiredTargets {
requiredNames[target] = struct{}{}
requiredNames.Insert(target)
}

// if a user specifies a template, overwrite what is defined in the config to allow
// jobs to gradually transition from external to ci-operator to being provided as
// steps
excludedTests := sets.NewString()
for _, target := range templates {
excludedTests.Insert(target.Name)
}

var buildClient steps.BuildClient
Expand Down Expand Up @@ -155,13 +165,19 @@ func FromConfig(
buildSteps = append(buildSteps, initialReleaseStep)

} else if rawStep.TestStepConfiguration != nil && rawStep.TestStepConfiguration.OpenshiftInstallerClusterTestConfiguration != nil && rawStep.TestStepConfiguration.OpenshiftInstallerClusterTestConfiguration.Upgrade {
if excludedTests.Has(rawStep.TestStepConfiguration.As) {
continue
}
var err error
step, err = clusterinstall.E2ETestStep(*rawStep.TestStepConfiguration.OpenshiftInstallerClusterTestConfiguration, *rawStep.TestStepConfiguration, params, podClient, templateClient, secretGetter, artifactDir, jobSpec)
if err != nil {
return nil, nil, fmt.Errorf("unable to create end to end test step: %v", err)
}

} else if rawStep.TestStepConfiguration != nil {
if excludedTests.Has(rawStep.TestStepConfiguration.As) {
continue
}
step = steps.TestStep(*rawStep.TestStepConfiguration, config.Resources, podClient, artifactDir, jobSpec)
}

Expand Down
Loading