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 TestProjectStatus(t *testing.T) { testCases := map[string]struct { Path string Extra []runtime.Object ErrFn func(error) bool Contains []string Time time.Time }{ "missing project": { ErrFn: func(err error) bool { return errors.IsNotFound(err) }, }, "empty project with display name": { Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{ Name: "example", Namespace: "", Annotations: map[string]string{ projectapi.ProjectDisplayName: "Test", }, }, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project Test (example) on server https://example.com:8443\n", "You have no services, deployment configs, or build configs.", }, }, "empty service": { Path: "../../../../test/fixtures/app-scenarios/k8s-service-with-nothing.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/empty-service", "<initializing>:5432", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, }, "service with RC": { Path: "../../../../test/fixtures/app-scenarios/k8s-unserviced-rc.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/database-rc", "rc/database-rc-1 runs mysql", "0/1 pods growing to 1", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, }, "rc with unmountable and missing secrets": { Path: "../../../../pkg/api/graph/test/bad_secret_with_just_rc.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "rc/my-rc runs centos/mysql-56-centos7", "0/1 pods growing to 1", "rc/my-rc is attempting to mount a missing secret secret/dne", }, }, "dueling rcs": { Path: "../../../../pkg/api/graph/test/dueling-rcs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "dueling-rc", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "rc/rc-1 is competing for pod/conflicted-pod with rc/rc-2", "rc/rc-2 is competing for pod/conflicted-pod with rc/rc-1", }, }, "service with pod": { Path: "../../../../pkg/api/graph/test/service-with-pod.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/frontend-app", "pod/frontend-app-1-bjwh8 runs openshift/ruby-hello-world", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, }, "standalone rc": { Path: "../../../../pkg/api/graph/test/bare-rc.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", " rc/database-1 runs centos/mysql-56-centos7", "rc/frontend-rc-1 runs openshift/ruby-hello-world", }, }, "unstarted build": { Path: "../../../../test/fixtures/app-scenarios/new-project-no-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/sinatra-example-2 - 172.30.17.48:8080", "builds git://github.com", "with docker.io/centos/ruby-22-centos7:latest", "not built yet", "deployment #1 waiting on image or update", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, }, "unpushable build": { Path: "../../../../pkg/api/graph/test/unpushable-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "bc/ruby-hello-world is pushing to istag/ruby-hello-world:latest, but the administrator has not configured the integrated Docker registry.", }, }, "bare-bc-can-push": { Path: "../../../../pkg/api/graph/test/bare-bc-can-push.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ // this makes sure that status knows this can push. If it fails, there's a "(can't push image)" next to like #8 " hours\n build #7", }, Time: mustParseTime("2015-12-17T20:36:15Z"), }, "cyclical build": { Path: "../../../../pkg/api/graph/test/circular.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "Cycle detected in build configurations:", }, }, "running build": { Path: "../../../../test/fixtures/app-scenarios/new-project-one-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/sinatra-example-1 - 172.30.17.47:8080", "builds git://github.com", "with docker.io/centos/ruby-22-centos7:latest", "build #1 running for about a minute", "deployment #1 waiting on image or update", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "a/b test DeploymentConfig": { Path: "../../../../test/fixtures/app-scenarios/new-project-two-deployment-configs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/sinatra-app-example - 172.30.17.49:8080", "sinatra-app-example-a deploys", "sinatra-app-example-b deploys", "with docker.io/centos/ruby-22-centos7:latest", "build #1 running for about a minute", "- 7a4f354: Prepare v1beta3 Template types (Roy Programmer <*****@*****.**>)", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "with real deployments": { Path: "../../../../test/fixtures/app-scenarios/new-project-deployed-app.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "svc/database - 172.30.17.240:5434 -> 3306", "https://www.test.com (redirects) to pod port 8080 (svc/frontend)", "http://frontend-example.router.default.svc.cluster.local to pod port 8080 (!)", "svc/database-external (all nodes):31000 -> 3306", "database test deploys", "frontend deploys", "with docker.io/centos/ruby-22-centos7:latest", "deployment #3 pending on image", "deployment #2 failed less than a second ago: unable to contact server - 0/1 pods", "deployment #1 deployed less than a second ago", "test deployment #2 running for 7 seconds - 2/1 pods", "test deployment #1 deployed 8 seconds ago", "* bc/ruby-sample-build is pushing to istag/origin-ruby-sample:latest, but the image stream for that tag does not exist.", "* The image trigger for dc/frontend will have no effect because is/origin-ruby-sample does not exist", "* route/frontend was not accepted by router \"other\": (HostAlreadyClaimed)", "* dc/database has no readiness probe to verify pods are ready to accept traffic or ensure deployment is successful.", "View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.", }, Time: mustParseTime("2015-04-07T04:12:25Z"), }, "restarting pod": { Path: "../../../api/graph/test/restarting-pod.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ `container "ruby-helloworld" in pod/frontend-app-1-bjwh8 has restarted 8 times`, `container "gitlab-ce" in pod/gitlab-ce-1-lc411 is crash-looping`, `oc logs -p gitlab-ce-1-lc411 -c gitlab-ce`, // verifies we print the log command `policycommand example default`, // verifies that we print the help command }, }, "cross namespace reference": { Path: "../../../api/graph/test/different-project-image-deployment.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ // If there was a warning we wouldn't get the following message. Since we ignore cross-namespace // links by default, there should be no warning here. `View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'.`, }, }, } oldTimeFn := timeNowFn defer func() { timeNowFn = oldTimeFn }() for k, test := range testCases { timeNowFn = func() time.Time { if !test.Time.IsZero() { return test.Time } return time.Now() } o := ktestclient.NewObjects(kapi.Scheme, kapi.Codecs.UniversalDecoder()) if len(test.Path) > 0 { if err := ktestclient.AddObjectsFromPath(test.Path, o, kapi.Codecs.UniversalDecoder()); err != nil { t.Fatal(err) } } for _, obj := range test.Extra { o.Add(obj) } oc, kc := testclient.NewFixtureClients(o) d := ProjectStatusDescriber{C: oc, K: kc, Server: "https://example.com:8443", Suggest: true, LogsCommandName: "oc logs -p", SecurityPolicyCommandFormat: "policycommand %s %s"} out, err := d.Describe("example", "") if !test.ErrFn(err) { t.Errorf("%s: unexpected error: %v", k, err) } if err != nil { continue } for _, s := range test.Contains { if !strings.Contains(out, s) { t.Errorf("%s: did not have %q:\n%s\n---", k, s, out) } } } }
func TestProjectStatus(t *testing.T) { testCases := map[string]struct { Path string Extra []runtime.Object ErrFn func(error) bool Contains []string Time time.Time }{ "missing project": { ErrFn: func(err error) bool { return errors.IsNotFound(err) }, }, "empty project with display name": { Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{ Name: "example", Namespace: "", Annotations: map[string]string{ projectapi.ProjectDisplayName: "Test", }, }, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project Test (example) on server https://example.com:8443\n", "You have no services, deployment configs, or build configs.", }, }, "empty service": { Path: "../../../../test/fixtures/app-scenarios/k8s-service-with-nothing.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/empty-service", "<initializing>:5432", "To see more, use", }, }, "service with RC": { Path: "../../../../test/fixtures/app-scenarios/k8s-unserviced-rc.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/database-rc", "rc/database-rc-1 runs mysql", "0/1 pods growing to 1", "To see more, use", }, }, "rc with unmountable and missing secrets": { Path: "../../../../pkg/api/graph/test/bad_secret_with_just_rc.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "rc/my-rc runs openshift/mysql-55-centos7", "0/1 pods growing to 1", "rc/my-rc is attempting to mount a secret secret/existing-secret disallowed by sa/default", "rc/my-rc is attempting to mount a secret secret/dne disallowed by sa/default", "rc/my-rc is attempting to mount a missing secret secret/dne", }, }, "dueling rcs": { Path: "../../../../pkg/api/graph/test/dueling-rcs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "dueling-rc", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "rc/rc-1 is competing for pod/conflicted-pod with rc/rc-2", "rc/rc-2 is competing for pod/conflicted-pod with rc/rc-1", }, }, "service with pod": { Path: "../../../../pkg/api/graph/test/service-with-pod.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/frontend-app", "pod/frontend-app-1-bjwh8 runs openshift/ruby-hello-world", "To see more, use", }, }, "standalone rc": { Path: "../../../../pkg/api/graph/test/bare-rc.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", " rc/database-1 runs openshift/mysql-55-centos7", "rc/frontend-rc-1 runs openshift/ruby-hello-world", }, }, "unstarted build": { Path: "../../../../test/fixtures/app-scenarios/new-project-no-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/sinatra-example-2 - 172.30.17.48:8080", "builds git://github.com", "with docker.io/openshift/ruby-20-centos7:latest", "not built yet", "#1 deployment waiting on image or update", "To see more, use", }, }, "unpushable build": { Path: "../../../../pkg/api/graph/test/unpushable-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "bc/ruby-hello-world is pushing to imagestreamtag/ruby-hello-world:latest that is using is/ruby-hello-world, but the administrator has not configured the integrated Docker registry.", }, }, "cyclical build": { Path: "../../../../pkg/api/graph/test/circular.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "Cycle detected in build configurations:", }, }, "running build": { Path: "../../../../test/fixtures/app-scenarios/new-project-one-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/sinatra-example-1 - 172.30.17.47:8080", "builds git://github.com", "with docker.io/openshift/ruby-20-centos7:latest", "build 1 running for about a minute", "#1 deployment waiting on image or update", "To see more, use", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "a/b test DeploymentConfig": { Path: "../../../../test/fixtures/app-scenarios/new-project-two-deployment-configs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/sinatra-app-example - 172.30.17.49:8080", "sinatra-app-example-a deploys", "sinatra-app-example-b deploys", "with docker.io/openshift/ruby-20-centos7:latest", "build 1 running for about a minute", "- 7a4f354: Prepare v1beta3 Template types (Roy Programmer <*****@*****.**>)", "To see more, use", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "with real deployments": { Path: "../../../../test/fixtures/app-scenarios/new-project-deployed-app.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/database - 172.30.17.240:5434 -> 3306", "service/frontend - 172.30.17.154:5432 -> 8080", "database deploys", "frontend deploys", "with docker.io/openshift/ruby-20-centos7:latest", "#2 deployment failed less than a second ago: unable to contact server - 0/1 pods", "#2 deployment running for 7 seconds - 2/1 pods", "#1 deployed 8 seconds ago", "#1 deployed less than a second ago", "To see more, use", }, Time: mustParseTime("2015-04-07T04:12:25Z"), }, } oldTimeFn := timeNowFn defer func() { timeNowFn = oldTimeFn }() for k, test := range testCases { timeNowFn = func() time.Time { if !test.Time.IsZero() { return test.Time } return time.Now() } 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) } } for _, obj := range test.Extra { o.Add(obj) } oc, kc := testclient.NewFixtureClients(o) d := ProjectStatusDescriber{C: oc, K: kc, Server: "https://example.com:8443"} out, err := d.Describe("example", "") if !test.ErrFn(err) { t.Errorf("%s: unexpected error: %v", k, err) } if err != nil { continue } for _, s := range test.Contains { if !strings.Contains(out, s) { t.Errorf("%s: did not have %q:\n%s\n---", k, s, out) } } } }
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) } } } } }
func TestProjectStatus(t *testing.T) { testCases := map[string]struct { Path string Extra []runtime.Object ErrFn func(error) bool Contains []string Time time.Time }{ "missing project": { ErrFn: func(err error) bool { return errors.IsNotFound(err) }, }, "empty project with display name": { Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{ Name: "example", Namespace: "", Annotations: map[string]string{ projectapi.ProjectDisplayName: "Test", }, }, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project Test (example)\n", "You have no services, deployment configs, or build configs.", }, }, "empty service": { Path: "../../../../test/fixtures/app-scenarios/k8s-service-with-nothing.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example\n", "service empty-service", "<initializing>:5432", "To see more, use", }, }, "service with RC": { Path: "../../../../test/fixtures/app-scenarios/k8s-unserviced-rc.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example\n", "service database-rc", "rc/database-rc-1 runs mysql", "0/1 pods growing to 1", "To see more, use", }, }, "unstarted build": { Path: "../../../../test/fixtures/app-scenarios/new-project-no-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example\n", "service sinatra-example-2 - 172.30.17.48:8080", "builds git://github.com", "with docker.io/openshift/ruby-20-centos7:latest", "not built yet", "#1 deployment waiting on image or update", "To see more, use", }, }, "running build": { Path: "../../../../test/fixtures/app-scenarios/new-project-one-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example\n", "service sinatra-example-1 - 172.30.17.47:8080", "builds git://github.com", "with docker.io/openshift/ruby-20-centos7:latest", "build 1 running for about a minute", "#1 deployment waiting on image or update", "To see more, use", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "a/b test DeploymentConfig": { Path: "../../../../test/fixtures/app-scenarios/new-project-two-deployment-configs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example\n", "service sinatra-app-example - 172.30.17.49:8080", "sinatra-app-example-a deploys", "sinatra-app-example-b deploys", "with docker.io/openshift/ruby-20-centos7:latest", "build 1 running for about a minute", "- 7a4f354: Prepare v1beta3 Template types (Roy Programmer <*****@*****.**>)", "To see more, use", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "with real deployments": { Path: "../../../../test/fixtures/app-scenarios/new-project-deployed-app.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example\n", "service database - 172.30.17.240:5434 -> 3306", "service frontend - 172.30.17.154:5432 -> 8080", "database deploys", "frontend deploys", "with docker.io/openshift/ruby-20-centos7:latest", "#2 deployment failed less than a second ago: unable to contact server - 0/1 pods", "#2 deployment running for 7 seconds - 2/1 pods", "#1 deployed 8 seconds ago", "#1 deployed less than a second ago", "To see more, use", }, Time: mustParseTime("2015-04-07T04:12:25Z"), }, } oldTimeFn := timeNowFn defer func() { timeNowFn = oldTimeFn }() for k, test := range testCases { timeNowFn = func() time.Time { if !test.Time.IsZero() { return test.Time } return time.Now() } 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) } } for _, obj := range test.Extra { o.Add(obj) } oc, kc := testclient.NewFixtureClients(o) d := ProjectStatusDescriber{C: oc, K: kc} out, err := d.Describe("example", "") if !test.ErrFn(err) { t.Errorf("%s: unexpected error: %v", k, err) } if err != nil { continue } for _, s := range test.Contains { if !strings.Contains(out, s) { t.Errorf("%s: did not have %q:\n%s\n---", k, s, out) } } t.Logf("\n%s", out) } }