func TestLimitVerifier(t *testing.T) { makeISForbiddenError := func(isName string, exceeded []kapi.ResourceName) error { if len(exceeded) == 0 { return nil } exceededStrings := []string{} for _, r := range exceeded { exceededStrings = append(exceededStrings, string(r)) } sort.Strings(exceededStrings) err := fmt.Errorf("exceeded %s", strings.Join(exceededStrings, ",")) return kapierrors.NewForbidden(api.Resource("ImageStream"), isName, err) } makeISEvaluator := func(maxImages, maxImageTags int64) func(string, *api.ImageStream) error { return func(ns string, is *api.ImageStream) error { limit := kapi.ResourceList{ api.ResourceImageStreamImages: *resource.NewQuantity(maxImages, resource.DecimalSI), api.ResourceImageStreamTags: *resource.NewQuantity(maxImageTags, resource.DecimalSI), } usage := admission.GetImageStreamUsage(is) if less, exceeded := kquota.LessThanOrEqual(usage, limit); !less { return makeISForbiddenError(is.Name, exceeded) } return nil } } tests := []struct { name string isEvaluator func(string, *api.ImageStream) error is api.ImageStream expected field.ErrorList }{ { name: "no limit", is: api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": { Items: []api.TagEvent{ { DockerImageReference: testutil.MakeDockerImageReference("test", "is", testutil.BaseImageWith1LayerDigest), Image: testutil.BaseImageWith1LayerDigest, }, }, }, }, }, }, }, { name: "below limit", is: api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": { Items: []api.TagEvent{ { DockerImageReference: testutil.MakeDockerImageReference("test", "is", testutil.BaseImageWith1LayerDigest), Image: testutil.BaseImageWith1LayerDigest, }, }, }, }, }, }, isEvaluator: makeISEvaluator(1, 0), }, { name: "exceed images", is: api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": { Items: []api.TagEvent{ { DockerImageReference: testutil.MakeDockerImageReference("test", "is", testutil.BaseImageWith1LayerDigest), Image: testutil.BaseImageWith1LayerDigest, }, }, }, "oldest": { Items: []api.TagEvent{ { DockerImageReference: testutil.MakeDockerImageReference("test", "is", testutil.BaseImageWith2LayersDigest), Image: testutil.BaseImageWith2LayersDigest, }, }, }, }, }, }, isEvaluator: makeISEvaluator(1, 0), expected: field.ErrorList{ field.Forbidden(field.NewPath("imageStream"), makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamImages}).Error()), }, }, { name: "exceed tags", is: api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: testutil.MakeDockerImageReference("test", "is", testutil.ChildImageWith2LayersDigest), }, }, }, }, }, isEvaluator: makeISEvaluator(0, 0), expected: field.ErrorList{ field.Forbidden(field.NewPath("imageStream"), makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamTags}).Error()), }, }, { name: "exceed images and tags", is: api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: testutil.MakeDockerImageReference("test", "other", testutil.BaseImageWith1LayerDigest), }, }, }, }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{ "latest": { Items: []api.TagEvent{ { DockerImageReference: testutil.MakeDockerImageReference("test", "other", testutil.BaseImageWith1LayerDigest), Image: testutil.BaseImageWith1LayerDigest, }, }, }, }, }, }, isEvaluator: makeISEvaluator(0, 0), expected: field.ErrorList{ field.Forbidden(field.NewPath("imageStream"), makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamImages, api.ResourceImageStreamTags}).Error()), }, }, } for _, tc := range tests { sar := &fakeSubjectAccessReviewRegistry{ allow: true, } tagVerifier := &TagVerifier{sar} s := &Strategy{ tagVerifier: tagVerifier, limitVerifier: &testutil.FakeImageStreamLimitVerifier{ ImageStreamEvaluator: tc.isEvaluator, }, defaultRegistry: &fakeDefaultRegistry{}, } ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) errList := s.Validate(ctx, &tc.is) if e, a := tc.expected, errList; !reflect.DeepEqual(e, a) { t.Errorf("%s: unexpected validation errors: %s", tc.name, diff.ObjectDiff(e, a)) } } }
func TestVerifyLimits(t *testing.T) { for _, tc := range []struct { name string maxUsage kapi.ResourceList is imageapi.ImageStream exceededResources []kapi.ResourceName }{ { name: "no limits", is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, }, { name: "zero limits", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamImages: resource.MustParse("0"), imageapi.ResourceImageStreamTags: resource.MustParse("0"), }, }, { name: "exceed images", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamImages: resource.MustParse("0"), imageapi.ResourceImageStreamTags: resource.MustParse("0"), }, is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, exceededResources: []kapi.ResourceName{imageapi.ResourceImageStreamImages}, }, { name: "exceed tags", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamTags: resource.MustParse("0"), }, is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, exceededResources: []kapi.ResourceName{imageapi.ResourceImageStreamTags}, }, { name: "exceed tags and images", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamTags: resource.MustParse("1"), imageapi.ResourceImageStreamImages: resource.MustParse("0"), }, is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), }, }, "good": { Name: "good", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, exceededResources: []kapi.ResourceName{ imageapi.ResourceImageStreamTags, imageapi.ResourceImageStreamImages, }, }, } { limitRange := &kapi.LimitRange{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "limitrange", }, Spec: kapi.LimitRangeSpec{ Limits: []kapi.LimitRangeItem{ { Type: imageapi.LimitTypeImageStream, Max: tc.maxUsage, }, }, }, } verifier := &limitVerifier{ limiter: LimitRangesForNamespaceFunc(func(ns string) ([]*kapi.LimitRange, error) { return []*kapi.LimitRange{limitRange}, nil }), } err := verifier.VerifyLimits("test", &tc.is) if len(tc.exceededResources) > 0 && err == nil { t.Errorf("[%s] unexpected non-error while following resources should fail: %v", tc.name, tc.exceededResources) continue } if len(tc.exceededResources) == 0 && err != nil { t.Errorf("[%s] unexpected error: %v", tc.name, err) continue } for _, r := range tc.exceededResources { if !strings.Contains(err.Error(), string(r)) { t.Errorf("[%s] expected resource %q not found in error message: %v", tc.name, r, err) } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("[%s] error %q not matched by IsErrorQuotaExceeded", tc.name, err) } } } }
func TestGetImageReferenceForObjectReference(t *testing.T) { for _, tc := range []struct { name string namespace string objRef kapi.ObjectReference expectedString string expectedError bool }{ { name: "isimage without namespace", objRef: kapi.ObjectReference{ Kind: "ImageStreamImage", Name: imageapi.MakeImageStreamImageName("is", imagetest.BaseImageWith1LayerDigest), }, expectedString: "is@" + imagetest.BaseImageWith1LayerDigest, }, { name: "isimage with a fallback namespace", namespace: "fallback", objRef: kapi.ObjectReference{ Kind: "ImageStreamImage", Name: imageapi.MakeImageStreamImageName("is", imagetest.BaseImageWith1LayerDigest), }, expectedString: "fallback/is@" + imagetest.BaseImageWith1LayerDigest, }, { name: "isimage with namespace set", namespace: "fallback", objRef: kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "ns", Name: imageapi.MakeImageStreamImageName("is", imagetest.BaseImageWith1LayerDigest), }, expectedString: "ns/is@" + imagetest.BaseImageWith1LayerDigest, }, { name: "isimage missing id", objRef: kapi.ObjectReference{ Kind: "ImageStreamImage", Name: imagetest.InternalRegistryURL + "/is", }, expectedError: true, }, { name: "isimage with a tag", objRef: kapi.ObjectReference{ Kind: "ImageStreamImage", Name: imagetest.InternalRegistryURL + "/is:latest", }, expectedError: true, }, { name: "istag without namespace", objRef: kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "is:latest", }, expectedString: "is:latest", }, { name: "istag with fallback namespace", namespace: "fallback", objRef: kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "is:latest", }, expectedString: "fallback/is:latest", }, { name: "istag with namespace set", namespace: "fallback", objRef: kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "ns", Name: "is:latest", }, expectedString: "ns/is:latest", }, { name: "istag with missing tag", objRef: kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "is", }, expectedError: true, }, { name: "istag with image ID", objRef: kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "is@" + imagetest.BaseImageWith1LayerDigest, }, expectedError: true, }, { name: "dockerimage without registry url", objRef: kapi.ObjectReference{ Kind: "DockerImage", Namespace: "ns", Name: "repo@" + imagetest.BaseImageWith1LayerDigest, }, expectedString: "docker.io/repo@" + imagetest.BaseImageWith1LayerDigest, }, { name: "dockerimage with a default tag", objRef: kapi.ObjectReference{ Kind: "DockerImage", Namespace: "ns", Name: "library/repo:latest", }, expectedString: "docker.io/repo", }, { name: "dockerimage with a non-default tag", objRef: kapi.ObjectReference{ Kind: "DockerImage", Namespace: "ns", Name: "repo:tag", }, expectedString: "docker.io/repo:tag", }, { name: "dockerimage referencing docker image", objRef: kapi.ObjectReference{ Kind: "DockerImage", Name: "index.docker.io/repo@" + imagetest.BaseImageWith1LayerDigest, }, expectedString: "docker.io/repo@" + imagetest.BaseImageWith1LayerDigest, }, { name: "dockerimage without tag or id", objRef: kapi.ObjectReference{ Kind: "DockerImage", Name: "index.docker.io/user/repo", }, expectedString: "docker.io/user/repo", }, { name: "dockerimage with internal registry", objRef: kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, expectedString: imagetest.InternalRegistryURL + "/test/is@" + imagetest.BaseImageWith1LayerDigest, }, { name: "bad king", objRef: kapi.ObjectReference{ Kind: "dockerImage", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, expectedError: true, }, } { res, err := GetImageReferenceForObjectReference(tc.namespace, &tc.objRef) if tc.expectedError && err == nil { t.Errorf("[%s] got unexpected non-error", tc.name) } if !tc.expectedError { if err != nil { t.Errorf("[%s] got unexpected error: %v", tc.name, err) } if res != tc.expectedString { t.Errorf("[%s] got unexpected results (%q != %q)", tc.name, res, tc.expectedString) } } } }
func TestGetImageStreamUsage(t *testing.T) { for _, tc := range []struct { name string is imageapi.ImageStream expectedTags int64 expectedImages int64 }{ { name: "empty", }, { name: "single tag", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "latest": { Name: "latest", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: "openshift/base:v1", }, }, }, }, }, expectedTags: 1, }, { name: "single image", is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, expectedImages: 1, }, { name: "tag and image", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "shared", Name: fmt.Sprintf("is@%s", imagetest.MiscImageDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, expectedTags: 1, expectedImages: 1, }, { name: "two images under one tag", is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "sharedlayer", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, { DockerImageReference: imagetest.MakeDockerImageReference("test", "sharedlayer", imagetest.BaseImageWith2LayersDigest), Image: imagetest.BaseImageWith2LayersDigest, }, }, }, }, }, }, expectedImages: 2, }, { name: "two different tags", is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "foo": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "sharedlayer", imagetest.BaseImageWith2LayersDigest), Image: imagetest.BaseImageWith2LayersDigest, }, }, }, "bar": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "sharedlayer", imagetest.ChildImageWith3LayersDigest), Image: imagetest.ChildImageWith3LayersDigest, }, }, }, }, }, }, expectedImages: 2, }, { name: "the same image under different tags", is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), Image: imagetest.ChildImageWith2LayersDigest, }, }, }, "foo": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("nm", "repository", imagetest.ChildImageWith2LayersDigest), Image: imagetest.ChildImageWith2LayersDigest, }, }, }, }, }, }, expectedImages: 1, }, { name: "two non-canonical references", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repo:latest", }, }, "same": { Name: "same", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: "index.docker.io/repo", }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "new": { Items: []imageapi.TagEvent{ { DockerImageReference: "docker.io/library/repo:latest", Image: imagetest.ChildImageWith3LayersDigest, }, }, }, }, }, }, expectedTags: 1, expectedImages: 1, }, { name: "the same image in both spec and status", is: imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "noshared", }, Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), Image: imagetest.ChildImageWith2LayersDigest, }, }, }, }, }, }, expectedTags: 1, expectedImages: 1, }, { name: "imagestreamtag and dockerimage references", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "ist": { Name: "ist", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "shared", Name: "is:latest", }, }, "dockerimage": { Name: "dockerimage", From: &kapi.ObjectReference{ Kind: "DockerImage", Namespace: "shared", Name: fmt.Sprintf("is:latest"), }, }, }, }, }, expectedTags: 2, }, { name: "dockerimage reference tagged in status", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "dockerimage": { Name: "dockerimage", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, expectedTags: 1, expectedImages: 1, }, { name: "wrong spec image references", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "badkind": { Name: "badkind", From: &kapi.ObjectReference{ Kind: "unknown", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, }, "badistag": { Name: "badistag", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "shared", Name: "is", }, }, "badisimage": { Name: "badistag", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "shared", Name: "is:tag", }, }, "good": { Name: "good", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, }, }, }, }, expectedTags: 1, }, { name: "identical tags with fallback namespace", is: imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "fallback", Name: "is", }, Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "havingnamespace": { Name: "havingnamespace", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "fallback", Name: "other:tag", }, }, "lackingnamespace": { Name: "lackingnamespace", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "other:tag", }, }, }, }, }, expectedTags: 1, }, { name: "identical tags without fallback namespace", is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "havingnamespace": { Name: "havingnamespace", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "ns", Name: "other:tag", }, }, "lackingnamespace": { Name: "lackingnamespace", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "other:tag", }, }, }, }, }, expectedTags: 2, }, } { usage := GetImageStreamUsage(&tc.is) expectedUsage := kapi.ResourceList{ imageapi.ResourceImageStreamTags: *resource.NewQuantity(tc.expectedTags, resource.DecimalSI), imageapi.ResourceImageStreamImages: *resource.NewQuantity(tc.expectedImages, resource.DecimalSI), } if len(usage) != len(expectedUsage) { t.Errorf("[%s] got unexpected number of limits (%d != %d)", tc.name, len(usage), len(expectedUsage)) } for r, expVal := range expectedUsage { val, exists := usage[r] if !exists { t.Errorf("[%s] expected resource %s is missing", tc.name, r) continue } if val.Cmp(expVal) != 0 { t.Errorf("[%s] got unexpected value for resource %s (%s != %s)", tc.name, r, val.String(), expVal.String()) } } for r := range usage { if _, exists := expectedUsage[r]; !exists { t.Errorf("[%s] got unexpected resource %s", tc.name, r) } } } }
func TestImageStreamTagEvaluatorUsage(t *testing.T) { for _, tc := range []struct { name string iss []imageapi.ImageStream ist imageapi.ImageStreamTag expectedISCount int64 }{ { name: "empty image stream", iss: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, Status: imageapi.ImageStreamStatus{}, }, }, ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is:dest", }, Tag: &imageapi.TagReference{ Name: "dest", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "shared", Name: "is@" + imagetest.MiscImageDigest, }, }, }, expectedISCount: 0, }, { name: "no image stream", ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is:dest", }, Tag: &imageapi.TagReference{ Name: "dest", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "shared", Name: "is@" + imagetest.MiscImageDigest, }, }, }, expectedISCount: 1, }, { name: "no image stream using image stream tag", ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is:dest", }, Tag: &imageapi.TagReference{ Name: "dest", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "shared", Name: "is:latest", }, }, }, expectedISCount: 1, }, { name: "no tag given", ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is:dest", }, Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: imagetest.MiscImageDigest, Annotations: map[string]string{imageapi.ManagedByOpenShiftAnnotation: "true"}, }, DockerImageReference: imagetest.MakeDockerImageReference("shared", "is", imagetest.MiscImageDigest), DockerImageManifest: imagetest.MiscImageDigest, }, }, expectedISCount: 1, }, { name: "missing from", ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is:dest", }, Tag: &imageapi.TagReference{ Name: "dest", }, Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: imagetest.MiscImageDigest, Annotations: map[string]string{imageapi.ManagedByOpenShiftAnnotation: "true"}, }, DockerImageReference: imagetest.MakeDockerImageReference("test", "dest", imagetest.MiscImageDigest), DockerImageManifest: imagetest.MiscImage, }, }, expectedISCount: 1, }, { name: "update existing tag", iss: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "havingtag", }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "havingtag", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, }, ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "havingtag:latest", }, Tag: &imageapi.TagReference{ Name: "latest", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Namespace: "shared", Name: "is@" + imagetest.ChildImageWith2LayersDigest, }, }, }, expectedISCount: 0, }, { name: "add a new tag with 2 image streams", iss: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, }, { ObjectMeta: kapi.ObjectMeta{ Namespace: "other", Name: "is2", }, }, }, ist: imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "destis:latest", }, Tag: &imageapi.TagReference{ Name: "latest", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Namespace: "other", Name: "is2:latest", }, }, }, expectedISCount: 1, }, } { fakeClient := &testclient.Fake{} fakeClient.AddReactor("get", "imagestreams", imagetest.GetFakeImageStreamGetHandler(t, tc.iss...)) evaluator := NewImageStreamTagEvaluator(fakeClient, fakeClient) usage := evaluator.Usage(&tc.ist) expectedUsage := imagetest.ExpectedResourceListFor(tc.expectedISCount) expectedResources := kquota.ResourceNames(expectedUsage) if len(usage) != len(expectedUsage) { t.Errorf("[%s]: got unexpected number of computed resources: %d != %d", tc.name, len(usage), len(expectedResources)) } masked := kquota.Mask(usage, expectedResources) if len(masked) != len(expectedUsage) { for k := range usage { if _, exists := masked[k]; !exists { t.Errorf("[%s]: got unexpected resource %q from Usage() method", tc.name, k) } } for k := range expectedUsage { if _, exists := masked[k]; !exists { t.Errorf("[%s]: expected resource %q not computed", tc.name, k) } } } for rname, expectedValue := range expectedUsage { if v, exists := masked[rname]; exists { if v.Cmp(expectedValue) != 0 { t.Errorf("[%s]: got unexpected usage for %q: %s != %s", tc.name, rname, v.String(), expectedValue.String()) } } } } }