package describe import ( "bytes" "io/ioutil" "reflect" "strings" "testing" "time" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" kctl "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/runtime" "github.com/openshift/origin/pkg/api" authorizationapi "github.com/openshift/origin/pkg/authorization/api" buildapi "github.com/openshift/origin/pkg/build/api" deployapi "github.com/openshift/origin/pkg/deploy/api" imageapi "github.com/openshift/origin/pkg/image/api" oauthapi "github.com/openshift/origin/pkg/oauth/api" projectapi "github.com/openshift/origin/pkg/project/api" securityapi "github.com/openshift/origin/pkg/security/api" templateapi "github.com/openshift/origin/pkg/template/api" ) // PrinterCoverageExceptions is the list of API types that do NOT have corresponding printers // If you add something to this list, explain why it doesn't need printing. waaaa is not a valid // reason. var PrinterCoverageExceptions = []reflect.Type{ reflect.TypeOf(&imageapi.DockerImage{}), // not a top level resource reflect.TypeOf(&imageapi.ImageStreamImport{}), // normal users don't ever look at these reflect.TypeOf(&buildapi.BuildLog{}), // just a marker type reflect.TypeOf(&buildapi.BuildLogOptions{}), // just a marker type reflect.TypeOf(&deployapi.DeploymentRequest{}), // normal users don't ever look at these reflect.TypeOf(&deployapi.DeploymentLog{}), // just a marker type reflect.TypeOf(&deployapi.DeploymentLogOptions{}), // just a marker type // these resources can't be "GET"ed, so we probably don't need a printer for them reflect.TypeOf(&authorizationapi.SubjectAccessReviewResponse{}), reflect.TypeOf(&authorizationapi.ResourceAccessReviewResponse{}), reflect.TypeOf(&authorizationapi.SubjectAccessReview{}), reflect.TypeOf(&imageapi.ImageSignature{}), reflect.TypeOf(&authorizationapi.ResourceAccessReview{}), reflect.TypeOf(&authorizationapi.LocalSubjectAccessReview{}), reflect.TypeOf(&authorizationapi.LocalResourceAccessReview{}), reflect.TypeOf(&authorizationapi.SelfSubjectRulesReview{}), reflect.TypeOf(&authorizationapi.SubjectRulesReview{}), reflect.TypeOf(&buildapi.BuildLog{}), reflect.TypeOf(&buildapi.BinaryBuildRequestOptions{}), reflect.TypeOf(&buildapi.BuildRequest{}), reflect.TypeOf(&buildapi.BuildLogOptions{}), reflect.TypeOf(&securityapi.PodSecurityPolicySubjectReview{}), reflect.TypeOf(&securityapi.PodSecurityPolicySelfSubjectReview{}), reflect.TypeOf(&securityapi.PodSecurityPolicyReview{}), reflect.TypeOf(&oauthapi.OAuthRedirectReference{}), } // MissingPrinterCoverageExceptions is the list of types that were missing printer methods when I started // You should never add to this list // TODO printers should be added for these types var MissingPrinterCoverageExceptions = []reflect.Type{ reflect.TypeOf(&deployapi.DeploymentConfigRollback{}), reflect.TypeOf(&imageapi.ImageStreamMapping{}), reflect.TypeOf(&projectapi.ProjectRequest{}), } func TestPrinterCoverage(t *testing.T) { printer := NewHumanReadablePrinter(kctl.PrintOptions{}) main: for _, apiType := range kapi.Scheme.KnownTypes(api.SchemeGroupVersion) { if !strings.Contains(apiType.PkgPath(), "github.com/openshift/origin") || strings.Contains(apiType.PkgPath(), "github.com/openshift/origin/vendor/") { continue } ptrType := reflect.PtrTo(apiType) for _, exception := range PrinterCoverageExceptions { if ptrType == exception { continue main } } for _, exception := range MissingPrinterCoverageExceptions { if ptrType == exception { continue main } } newStructValue := reflect.New(apiType) newStruct := newStructValue.Interface() if err := printer.PrintObj(newStruct.(runtime.Object), ioutil.Discard); (err != nil) && strings.Contains(err.Error(), "error: unknown type ") { t.Errorf("missing printer for %v. Check pkg/cmd/cli/describe/printer.go", apiType) } } } func TestFormatResourceName(t *testing.T) { tests := []struct { kind, name string want string }{ {"", "", ""}, {"", "name", "name"}, {"kind", "", "kind/"}, // should not happen in practice {"kind", "name", "kind/name"}, } for _, tt := range tests { if got := formatResourceName(tt.kind, tt.name, true); got != tt.want { t.Errorf("formatResourceName(%q, %q) = %q, want %q", tt.kind, tt.name, got, tt.want) } } } func TestPrintImageStream(t *testing.T) { buf := bytes.NewBuffer([]byte{}) streams := mockStreams() tests := []struct { name string stream *imageapi.ImageStream expectedOut string expectedErr error }{ { name: "less than three tags", stream: streams[0], expectedOut: "latest,other", }, { name: "three tags", stream: streams[1], expectedOut: "third,latest,other", }, { name: "more than three tags", stream: streams[2], expectedOut: "another,third,latest + 1 more...", }, } for _, test := range tests { if err := printImageStream(test.stream, buf, kctl.PrintOptions{}); err != test.expectedErr { t.Errorf("error mismatch: expected %v, got %v", test.expectedErr, err) continue } got := buf.String() buf.Reset() if !strings.Contains(got, test.expectedOut) { t.Errorf("unexpected output:\n%s\nexpected to contain: %s", got, test.expectedOut) continue } } } func mockStreams() []*imageapi.ImageStream { return []*imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{Name: "less-than-three-tags"}, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "other": { Items: []imageapi.TagEvent{ { DockerImageReference: "other-ref", Created: unversioned.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: "latest-ref", Created: unversioned.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, }, }, }, { ObjectMeta: kapi.ObjectMeta{Name: "three-tags"}, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "other": { Items: []imageapi.TagEvent{ { DockerImageReference: "other-ref", Created: unversioned.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: "latest-ref", Created: unversioned.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, "third": { Items: []imageapi.TagEvent{ { DockerImageReference: "third-ref", Created: unversioned.Date(2015, 9, 4, 13, 54, 0, 0, time.UTC), Image: "third-image", }, }, }, }, }, }, { ObjectMeta: kapi.ObjectMeta{Name: "more-than-three-tags"}, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "other": { Items: []imageapi.TagEvent{ { DockerImageReference: "other-ref", Created: unversioned.Date(2015, 9, 4, 13, 52, 0, 0, time.UTC), Image: "other-image", }, }, }, "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: "latest-ref", Created: unversioned.Date(2015, 9, 4, 13, 53, 0, 0, time.UTC), Image: "latest-image", }, }, }, "third": { Items: []imageapi.TagEvent{ { DockerImageReference: "third-ref", Created: unversioned.Date(2015, 9, 4, 13, 54, 0, 0, time.UTC), Image: "third-image", }, }, }, "another": { Items: []imageapi.TagEvent{ { DockerImageReference: "another-ref", Created: unversioned.Date(2015, 9, 4, 13, 55, 0, 0, time.UTC), Image: "another-image", }, }, }, }, }, }, } } func TestPrintTemplate(t *testing.T) { tests := []struct { template templateapi.Template want string }{ { templateapi.Template{ ObjectMeta: kapi.ObjectMeta{ Name: "name", Annotations: map[string]string{ "description": "description", }, }, Parameters: []templateapi.Parameter{{}}, Objects: []runtime.Object{&kapi.Pod{}}, }, "name\tdescription\t1 (1 blank)\t1\n", }, { templateapi.Template{ ObjectMeta: kapi.ObjectMeta{ Name: "long", Annotations: map[string]string{ "description": "the long description of this template is way way way way way way way way way way way way way too long", }, }, Parameters: []templateapi.Parameter{}, Objects: []runtime.Object{}, }, "long\tthe long description of this template is way way way way way way way way way...\t0 (all set)\t0\n", }, { templateapi.Template{ ObjectMeta: kapi.ObjectMeta{ Name: "multiline", Annotations: map[string]string{ "description": "Once upon a time\nthere was a template\nwith multiple\nlines\n", }, }, Parameters: []templateapi.Parameter{}, Objects: []runtime.Object{}, }, "multiline\tOnce upon a time...\t0 (all set)\t0\n", }, { templateapi.Template{ ObjectMeta: kapi.ObjectMeta{ Name: "trailingnewline", Annotations: map[string]string{ "description": "Next line please\n", }, }, Parameters: []templateapi.Parameter{}, Objects: []runtime.Object{}, }, "trailingnewline\tNext line please...\t0 (all set)\t0\n", }, { templateapi.Template{ ObjectMeta: kapi.ObjectMeta{ Name: "longmultiline", Annotations: map[string]string{ "description": "12345678901234567890123456789012345678901234567890123456789012345678901234567890123\n0", }, }, Parameters: []templateapi.Parameter{}, Objects: []runtime.Object{}, }, "longmultiline\t12345678901234567890123456789012345678901234567890123456789012345678901234567...\t0 (all set)\t0\n", }, } for i, test := range tests { buf := bytes.NewBuffer([]byte{}) err := printTemplate(&test.template, buf, kctl.PrintOptions{}) if err != nil { t.Errorf("[%d] unexpected error: %v", i, err) continue } got := buf.String() if got != test.want { t.Errorf("[%d] expected %q, got %q", i, test.want, got) continue } } }