Example #1
0
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)
			}
		}
	}
}
Example #3
0
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)
			}
		}
	}
}
Example #4
0
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())
				}
			}
		}
	}
}