Exemple #1
0
// AddTriggerEdges creates edges that point to named Docker image repositories for each image used in the deployment.
func AddTriggerEdges(g osgraph.MutableUniqueGraph, node *deploygraph.DeploymentConfigNode) *deploygraph.DeploymentConfigNode {
	podTemplate := node.DeploymentConfig.Spec.Template
	if podTemplate == nil {
		return node
	}

	deployapi.EachTemplateImage(
		&podTemplate.Spec,
		deployapi.DeploymentConfigHasTrigger(node.DeploymentConfig),
		func(image deployapi.TemplateImage, err error) {
			if err != nil {
				return
			}
			if image.From != nil {
				if len(image.From.Name) == 0 {
					return
				}
				name, tag, _ := imageapi.SplitImageStreamTag(image.From.Name)
				in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(image.From.Namespace, name, tag))
				g.AddEdge(in, node, TriggersDeploymentEdgeKind)
				return
			}

			tag := image.Ref.Tag
			image.Ref.Tag = ""
			in := imagegraph.EnsureDockerRepositoryNode(g, image.Ref.String(), tag)
			g.AddEdge(in, node, UsedInDeploymentEdgeKind)
		})

	return node
}
Exemple #2
0
// AddInputOutputEdges links the build config to other nodes for the images and source repositories it depends on.
func AddInputOutputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) *buildgraph.BuildConfigNode {
	output := node.BuildConfig.Parameters.Output
	to := output.To
	switch {
	case to != nil && len(to.Name) > 0:
		out := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(to.Namespace, node.BuildConfig.Namespace), to.Name, output.Tag))
		g.AddEdge(node, out, BuildOutputEdgeKind)
	case len(output.DockerImageReference) > 0:
		out := imagegraph.EnsureDockerRepositoryNode(g, output.DockerImageReference, output.Tag)
		g.AddEdge(node, out, BuildOutputEdgeKind)
	}

	if in := buildgraph.EnsureSourceRepositoryNode(g, node.BuildConfig.Parameters.Source); in != nil {
		g.AddEdge(in, node, BuildInputEdgeKind)
	}

	from := buildutil.GetImageStreamForStrategy(node.BuildConfig.Parameters.Strategy)
	if from != nil {
		switch from.Kind {
		case "DockerImage":
			if ref, err := imageapi.ParseDockerImageReference(from.Name); err == nil {
				tag := ref.Tag
				ref.Tag = ""
				in := imagegraph.EnsureDockerRepositoryNode(g, ref.String(), tag)
				g.AddEdge(in, node, BuildInputImageEdgeKind)
			}
		case "ImageStream":
			tag := imageapi.DefaultImageTag
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name, tag))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamTag":
			name, tag, _ := imageapi.SplitImageStreamTag(from.Name)
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), name, tag))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamImage":
			glog.V(4).Infof("Ignoring ImageStreamImage reference in BuildConfig %s/%s", node.BuildConfig.Namespace, node.BuildConfig.Name)
		}
	}
	return node
}
Exemple #3
0
// RunBuildChain contains all the necessary functionality for the OpenShift
// experimental build-chain command
func (o *BuildChainOptions) RunBuildChain() error {
	ist := imagegraph.MakeImageStreamTagObjectMeta(o.defaultNamespace, o.name, o.tag)
	desc, err := describe.NewChainDescriber(o.c, o.namespaces, o.output).Describe(ist, !o.triggerOnly)
	if err != nil {
		if _, isNotFoundErr := err.(describe.NotFoundErr); isNotFoundErr {
			// Try to get the imageStreamTag via a direct GET
			if _, getErr := o.t.ImageStreamTags(o.defaultNamespace).Get(o.name, o.tag); getErr != nil {
				return getErr
			}
			fmt.Printf("Image stream tag '%s:%s' in %q doesn't have any dependencies.\n", o.name, o.tag, o.defaultNamespace)
			return nil
		}
		return err
	}

	fmt.Println(desc)

	return nil
}
Exemple #4
0
func imageRefNode(g osgraph.MutableUniqueGraph, ref *kapi.ObjectReference, bc *buildapi.BuildConfig) graph.Node {
	if ref == nil {
		return nil
	}
	switch ref.Kind {
	case "DockerImage":
		if ref, err := imageapi.ParseDockerImageReference(ref.Name); err == nil {
			tag := ref.Tag
			ref.Tag = ""
			return imagegraph.EnsureDockerRepositoryNode(g, ref.String(), tag)
		}
	case "ImageStream":
		return imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(ref.Namespace, bc.Namespace), ref.Name, imageapi.DefaultImageTag))
	case "ImageStreamTag":
		return imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta2(defaultNamespace(ref.Namespace, bc.Namespace), ref.Name))
	case "ImageStreamImage":
		return imagegraph.FindOrCreateSyntheticImageStreamImageNode(g, imagegraph.MakeImageStreamImageObjectMeta(defaultNamespace(ref.Namespace, bc.Namespace), ref.Name))
	}
	return nil
}
Exemple #5
0
// AddInputOutputEdges links the build config to other nodes for the images and source repositories it depends on.
func AddInputOutputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) *buildgraph.BuildConfigNode {
	output := node.BuildConfig.Spec.Output
	to := output.To
	switch {
	case to == nil:
	case to.Kind == "DockerImage":
		out := imagegraph.EnsureDockerRepositoryNode(g, to.Name, "")
		g.AddEdge(node, out, BuildOutputEdgeKind)
	case to.Kind == "ImageStreamTag":
		out := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta2(defaultNamespace(to.Namespace, node.BuildConfig.Namespace), to.Name))
		g.AddEdge(node, out, BuildOutputEdgeKind)
	}

	if in := buildgraph.EnsureSourceRepositoryNode(g, node.BuildConfig.Spec.Source); in != nil {
		g.AddEdge(in, node, BuildInputEdgeKind)
	}

	from := buildutil.GetImageStreamForStrategy(node.BuildConfig.Spec.Strategy)
	if from != nil {
		switch from.Kind {
		case "DockerImage":
			if ref, err := imageapi.ParseDockerImageReference(from.Name); err == nil {
				tag := ref.Tag
				ref.Tag = ""
				in := imagegraph.EnsureDockerRepositoryNode(g, ref.String(), tag)
				g.AddEdge(in, node, BuildInputImageEdgeKind)
			}
		case "ImageStream":
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name, imageapi.DefaultImageTag))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamTag":
			in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta2(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		case "ImageStreamImage":
			in := imagegraph.FindOrCreateSyntheticImageStreamImageNode(g, imagegraph.MakeImageStreamImageObjectMeta(defaultNamespace(from.Namespace, node.BuildConfig.Namespace), from.Name))
			g.AddEdge(in, node, BuildInputImageEdgeKind)
		}
	}
	return node
}
func TestChainDescriber(t *testing.T) {
	tests := []struct {
		testName         string
		namespaces       sets.String
		output           string
		defaultNamespace string
		name             string
		tag              string
		path             string
		humanReadable    map[string]int
		dot              []string
		expectedErr      error
		includeInputImg  bool
	}{
		{
			testName:         "human readable test - single namespace",
			namespaces:       sets.NewString("test"),
			output:           "",
			defaultNamespace: "test",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/single-namespace-bcs.yaml",
			humanReadable: map[string]int{
				"imagestreamtag/ruby-20-centos7:latest":        1,
				"\tbc/ruby-hello-world":                        1,
				"\t\timagestreamtag/ruby-hello-world:latest":   1,
				"\tbc/ruby-sample-build":                       1,
				"\t\timagestreamtag/origin-ruby-sample:latest": 1,
			},
			expectedErr: nil,
		},
		{
			testName:         "dot test - single namespace",
			namespaces:       sets.NewString("test"),
			output:           "dot",
			defaultNamespace: "test",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/single-namespace-bcs.yaml",
			dot: []string{
				"digraph \"ruby-20-centos7:latest\" {",
				"// Node definitions.",
				"[label=\"BuildConfig|test/ruby-hello-world\"];",
				"[label=\"BuildConfig|test/ruby-sample-build\"];",
				"[label=\"ImageStreamTag|test/ruby-hello-world:latest\"];",
				"[label=\"ImageStreamTag|test/ruby-20-centos7:latest\"];",
				"[label=\"ImageStreamTag|test/origin-ruby-sample:latest\"];",
				"",
				"// Edge definitions.",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildInputImage,BuildTriggerImage\"];",
				"[label=\"BuildInputImage,BuildTriggerImage\"];",
				"}",
			},
			expectedErr: nil,
		},
		{
			testName:         "human readable test - multiple namespaces",
			namespaces:       sets.NewString("test", "master", "default"),
			output:           "",
			defaultNamespace: "master",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/multiple-namespaces-bcs.yaml",
			humanReadable: map[string]int{
				"<master imagestreamtag/ruby-20-centos7:latest>":         1,
				"\t<default bc/ruby-hello-world>":                        1,
				"\t\t<test imagestreamtag/ruby-hello-world:latest>":      1,
				"\t<test bc/ruby-sample-build>":                          1,
				"\t\t<another imagestreamtag/origin-ruby-sample:latest>": 1,
			},
			expectedErr: nil,
		},
		{
			testName:         "dot test - multiple namespaces",
			namespaces:       sets.NewString("test", "master", "default"),
			output:           "dot",
			defaultNamespace: "master",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/multiple-namespaces-bcs.yaml",
			dot: []string{
				"digraph \"ruby-20-centos7:latest\" {",
				"// Node definitions.",
				"[label=\"BuildConfig|default/ruby-hello-world\"];",
				"[label=\"BuildConfig|test/ruby-sample-build\"];",
				"[label=\"ImageStreamTag|test/ruby-hello-world:latest\"];",
				"[label=\"ImageStreamTag|master/ruby-20-centos7:latest\"];",
				"[label=\"ImageStreamTag|another/origin-ruby-sample:latest\"];",
				"",
				"// Edge definitions.",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildInputImage,BuildTriggerImage\"];",
				"[label=\"BuildInputImage,BuildTriggerImage\"];",
				"}",
			},
			expectedErr: nil,
		},
		{
			testName:         "human readable - multiple triggers - triggeronly",
			name:             "ruby-20-centos7",
			defaultNamespace: "test",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/multiple-trigger-bcs.yaml",
			namespaces:       sets.NewString("test"),
			humanReadable: map[string]int{
				"imagestreamtag/ruby-20-centos7:latest":   1,
				"\tbc/parent1":                            1,
				"\t\timagestreamtag/parent1img:latest":    1,
				"\t\t\tbc/child2":                         2,
				"\t\t\t\timagestreamtag/child2img:latest": 2,
				"\tbc/parent2":                            1,
				"\t\timagestreamtag/parent2img:latest":    1,
				"\t\t\tbc/child3":                         2,
				"\t\t\t\timagestreamtag/child3img:latest": 2,
				"\t\t\tbc/child1":                         1,
				"\t\t\t\timagestreamtag/child1img:latest": 1,
				"\tbc/parent3":                            1,
				"\t\timagestreamtag/parent3img:latest":    1,
			},
		},
		{
			testName:         "human readable - multiple triggers - trigger+input",
			name:             "ruby-20-centos7",
			defaultNamespace: "test",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/multiple-trigger-bcs.yaml",
			namespaces:       sets.NewString("test"),
			includeInputImg:  true,
			humanReadable: map[string]int{
				"imagestreamtag/ruby-20-centos7:latest":   1,
				"\tbc/parent1":                            1,
				"\t\timagestreamtag/parent1img:latest":    1,
				"\t\t\tbc/child1":                         2,
				"\t\t\t\timagestreamtag/child1img:latest": 2,
				"\t\t\tbc/child2":                         2,
				"\t\t\t\timagestreamtag/child2img:latest": 2,
				"\t\t\tbc/child3":                         3,
				"\t\t\t\timagestreamtag/child3img:latest": 3,
				"\tbc/parent2":                            1,
				"\t\timagestreamtag/parent2img:latest":    1,
				"\tbc/parent3":                            1,
				"\t\timagestreamtag/parent3img:latest":    1,
			},
		},
	}

	for _, test := range tests {
		o := ktestclient.NewObjects(kapi.Scheme, kapi.Scheme)
		if len(test.path) > 0 {
			if err := ktestclient.AddObjectsFromPath(test.path, o, kapi.Scheme); err != nil {
				t.Fatal(err)
			}
		}

		oc, _ := testclient.NewFixtureClients(o)
		ist := imagegraph.MakeImageStreamTagObjectMeta(test.defaultNamespace, test.name, test.tag)

		desc, err := NewChainDescriber(oc, test.namespaces, test.output).Describe(ist, test.includeInputImg)
		t.Logf("%s: output:\n%s\n\n", test.testName, desc)
		if err != test.expectedErr {
			t.Fatalf("%s: error mismatch: expected %v, got %v", test.testName, test.expectedErr, err)
		}

		got := strings.Split(desc, "\n")

		switch test.output {
		case "dot":
			if len(test.dot) != len(got) {
				t.Fatalf("%s: expected %d lines, got %d:\n%s", test.testName, len(test.dot), len(got), desc)
			}
			for _, expected := range test.dot {
				if !strings.Contains(desc, expected) {
					t.Errorf("%s: unexpected description:\n%s\nexpected line in it:\n%s", test.testName, desc, expected)
				}
			}
		case "":
			if lenReadable(test.humanReadable) != len(got) {
				t.Fatalf("%s: expected %d lines, got %d:\n%s", test.testName, lenReadable(test.humanReadable), len(got), desc)
			}
			for _, line := range got {
				if _, ok := test.humanReadable[line]; !ok {
					t.Errorf("%s: unexpected line: %s", test.testName, line)
				}
				test.humanReadable[line]--
			}
			for line, cnt := range test.humanReadable {
				if cnt != 0 {
					t.Errorf("%s: unexpected number of lines for [%s]: %d", test.testName, line, cnt)
				}
			}
		}
	}
}
func TestChainDescriber(t *testing.T) {
	tests := []struct {
		testName         string
		namespaces       kutil.StringSet
		output           string
		defaultNamespace string
		name             string
		tag              string
		path             string
		humanReadable    map[string]struct{}
		dot              []string
		expectedErr      error
	}{
		{
			testName:         "human readable test - single namespace",
			namespaces:       kutil.NewStringSet("test"),
			output:           "",
			defaultNamespace: "test",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/single-namespace-bcs.yaml",
			humanReadable: map[string]struct{}{
				"imagestreamtag/ruby-20-centos7:latest":        {},
				"\tbc/ruby-hello-world":                        {},
				"\t\timagestreamtag/ruby-hello-world:latest":   {},
				"\tbc/ruby-sample-build":                       {},
				"\t\timagestreamtag/origin-ruby-sample:latest": {},
			},
			expectedErr: nil,
		},
		{
			testName:         "dot test - single namespace",
			namespaces:       kutil.NewStringSet("test"),
			output:           "dot",
			defaultNamespace: "test",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/single-namespace-bcs.yaml",
			dot: []string{
				"digraph \"ruby-20-centos7:latest\" {",
				"// Node definitions.",
				"[label=\"BuildConfig|test/ruby-hello-world\"];",
				"[label=\"BuildConfig|test/ruby-sample-build\"];",
				"[label=\"ImageStreamTag|test/ruby-hello-world:latest\"];",
				"[label=\"ImageStreamTag|test/ruby-20-centos7:latest\"];",
				"[label=\"ImageStreamTag|test/origin-ruby-sample:latest\"];",
				"",
				"// Edge definitions.",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildInputImage\"];",
				"[label=\"BuildInputImage\"];",
				"}",
			},
			expectedErr: nil,
		},
		{
			testName:         "human readable test - multiple namespaces",
			namespaces:       kutil.NewStringSet("test", "master", "default"),
			output:           "",
			defaultNamespace: "master",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/multiple-namespaces-bcs.yaml",
			humanReadable: map[string]struct{}{
				"<master imagestreamtag/ruby-20-centos7:latest>":         {},
				"\t<default bc/ruby-hello-world>":                        {},
				"\t\t<test imagestreamtag/ruby-hello-world:latest>":      {},
				"\t<test bc/ruby-sample-build>":                          {},
				"\t\t<another imagestreamtag/origin-ruby-sample:latest>": {},
			},
			expectedErr: nil,
		},
		{
			testName:         "dot test - multiple namespaces",
			namespaces:       kutil.NewStringSet("test", "master", "default"),
			output:           "dot",
			defaultNamespace: "master",
			name:             "ruby-20-centos7",
			tag:              "latest",
			path:             "../../../../pkg/cmd/experimental/buildchain/test/multiple-namespaces-bcs.yaml",
			dot: []string{
				"digraph \"ruby-20-centos7:latest\" {",
				"// Node definitions.",
				"[label=\"BuildConfig|default/ruby-hello-world\"];",
				"[label=\"BuildConfig|test/ruby-sample-build\"];",
				"[label=\"ImageStreamTag|test/ruby-hello-world:latest\"];",
				"[label=\"ImageStreamTag|master/ruby-20-centos7:latest\"];",
				"[label=\"ImageStreamTag|another/origin-ruby-sample:latest\"];",
				"",
				"// Edge definitions.",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildOutput\"];",
				"[label=\"BuildInputImage\"];",
				"[label=\"BuildInputImage\"];",
				"}",
			},
			expectedErr: nil,
		},
	}

	for _, test := range tests {
		o := ktestclient.NewObjects(kapi.Scheme, kapi.Scheme)
		if len(test.path) > 0 {
			if err := ktestclient.AddObjectsFromPath(test.path, o, kapi.Scheme); err != nil {
				t.Fatal(err)
			}
		}

		oc, _ := testclient.NewFixtureClients(o)
		ist := imagegraph.MakeImageStreamTagObjectMeta(test.defaultNamespace, test.name, test.tag)

		desc, err := NewChainDescriber(oc, test.namespaces, test.output).Describe(ist)
		if err != test.expectedErr {
			t.Fatalf("%s: error mismatch: expected %v, got %v", test.testName, test.expectedErr, err)
		}

		got := strings.Split(desc, "\n")

		switch test.output {
		case "dot":
			if len(test.dot) != len(got) {
				t.Fatalf("%s: expected %d lines, got %d", test.testName, len(test.dot), len(got))
			}
			for _, expected := range test.dot {
				if !strings.Contains(desc, expected) {
					t.Errorf("%s: unexpected description:\n%s\nexpected line in it:\n%s", test.testName, desc, expected)
				}
			}
		case "":
			if len(test.humanReadable) != len(got) {
				t.Fatalf("%s: expected %d lines, got %d", test.testName, len(test.humanReadable), len(got))
			}
			for _, line := range got {
				if _, ok := test.humanReadable[line]; !ok {
					t.Errorf("%s: unexpected line: %s", test.testName, line)
				}
			}
		}
	}
}