Browse code

ImageStream limits should not start its own reflector

Use shared informer, simplify code in limiter.

Clayton Coleman authored on 2016/09/11 08:56:20
Showing 11 changed files
... ...
@@ -51,7 +51,6 @@ import (
51 51
 	deployconfiggenerator "github.com/openshift/origin/pkg/deploy/registry/generator"
52 52
 	deployrollback "github.com/openshift/origin/pkg/deploy/registry/rollback"
53 53
 	"github.com/openshift/origin/pkg/dockerregistry"
54
-	imageadmission "github.com/openshift/origin/pkg/image/admission"
55 54
 	"github.com/openshift/origin/pkg/image/importer"
56 55
 	imageimporter "github.com/openshift/origin/pkg/image/importer"
57 56
 	"github.com/openshift/origin/pkg/image/registry/image"
... ...
@@ -463,9 +462,8 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
463 463
 	checkStorageErr(err)
464 464
 	imageRegistry := image.NewRegistry(imageStorage)
465 465
 	imageSignatureStorage := imagesignature.NewREST(c.PrivilegedLoopbackOpenShiftClient.Images())
466
-	imageStreamLimitVerifier := imageadmission.NewLimitVerifier(c.KubeClient())
467 466
 	imageStreamSecretsStorage := imagesecret.NewREST(c.ImageStreamSecretClient())
468
-	imageStreamStorage, imageStreamStatusStorage, internalImageStreamStorage, err := imagestreametcd.NewREST(c.RESTOptionsGetter, c.RegistryNameFn, subjectAccessReviewRegistry, imageStreamLimitVerifier)
467
+	imageStreamStorage, imageStreamStatusStorage, internalImageStreamStorage, err := imagestreametcd.NewREST(c.RESTOptionsGetter, c.RegistryNameFn, subjectAccessReviewRegistry, c.LimitVerifier)
469 468
 	checkStorageErr(err)
470 469
 	imageStreamRegistry := imagestream.NewRegistry(imageStreamStorage, imageStreamStatusStorage, internalImageStreamStorage)
471 470
 	imageStreamMappingStorage := imagestreammapping.NewREST(imageRegistry, imageStreamRegistry, c.RegistryNameFn)
... ...
@@ -16,6 +16,7 @@ import (
16 16
 
17 17
 	"k8s.io/kubernetes/pkg/admission"
18 18
 	kapi "k8s.io/kubernetes/pkg/api"
19
+	kapierrors "k8s.io/kubernetes/pkg/api/errors"
19 20
 	"k8s.io/kubernetes/pkg/api/unversioned"
20 21
 	"k8s.io/kubernetes/pkg/apiserver"
21 22
 	"k8s.io/kubernetes/pkg/client/cache"
... ...
@@ -25,6 +26,7 @@ import (
25 25
 	clientadapter "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
26 26
 	sacontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
27 27
 	kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
28
+	"k8s.io/kubernetes/pkg/labels"
28 29
 	"k8s.io/kubernetes/pkg/runtime"
29 30
 	"k8s.io/kubernetes/pkg/serviceaccount"
30 31
 	kutilrand "k8s.io/kubernetes/pkg/util/rand"
... ...
@@ -105,6 +107,7 @@ type MasterConfig struct {
105 105
 	ProjectAuthorizationCache     *projectauth.AuthorizationCache
106 106
 	ProjectCache                  *projectcache.ProjectCache
107 107
 	ClusterQuotaMappingController *clusterquotamapping.ClusterQuotaMappingController
108
+	LimitVerifier                 imageadmission.LimitVerifier
108 109
 
109 110
 	// RequestContextMapper maps requests to contexts
110 111
 	RequestContextMapper kapi.RequestContextMapper
... ...
@@ -296,6 +299,23 @@ func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) {
296 296
 		Informers:                          informerFactory,
297 297
 	}
298 298
 
299
+	// ensure that the limit range informer will be started
300
+	informer := config.Informers.LimitRanges().Informer()
301
+	config.LimitVerifier = imageadmission.NewLimitVerifier(imageadmission.LimitRangesForNamespaceFunc(func(ns string) ([]*kapi.LimitRange, error) {
302
+		list, err := config.Informers.LimitRanges().Lister().LimitRanges(ns).List(labels.Everything())
303
+		if err != nil {
304
+			return nil, err
305
+		}
306
+		// the verifier must return an error
307
+		if len(list) == 0 && len(informer.LastSyncResourceVersion()) == 0 {
308
+			glog.V(4).Infof("LimitVerifier still waiting for ranges to load: %#v", informer)
309
+			forbiddenErr := kapierrors.NewForbidden(unversioned.GroupResource{Resource: "limitranges"}, "", fmt.Errorf("the server is still loading limit information"))
310
+			forbiddenErr.ErrStatus.Details.RetryAfterSeconds = 1
311
+			return nil, forbiddenErr
312
+		}
313
+		return list, nil
314
+	}))
315
+
299 316
 	return config, nil
300 317
 }
301 318
 
... ...
@@ -322,6 +342,7 @@ var (
322 322
 		serviceadmit.ExternalIPPluginName,
323 323
 		serviceadmit.RestrictedEndpointsPluginName,
324 324
 		imagepolicy.PluginName,
325
+		"ImagePolicyWebhook",
325 326
 		"LimitRanger",
326 327
 		"ServiceAccount",
327 328
 		"SecurityContextConstraint",
... ...
@@ -357,6 +378,7 @@ var (
357 357
 		serviceadmit.ExternalIPPluginName,
358 358
 		serviceadmit.RestrictedEndpointsPluginName,
359 359
 		imagepolicy.PluginName,
360
+		"ImagePolicyWebhook",
360 361
 		"LimitRanger",
361 362
 		"ServiceAccount",
362 363
 		"SecurityContextConstraint",
... ...
@@ -4,8 +4,10 @@ import (
4 4
 	"reflect"
5 5
 
6 6
 	kapi "k8s.io/kubernetes/pkg/api"
7
+	"k8s.io/kubernetes/pkg/api/errors"
7 8
 	"k8s.io/kubernetes/pkg/client/cache"
8 9
 	"k8s.io/kubernetes/pkg/controller/framework"
10
+	"k8s.io/kubernetes/pkg/labels"
9 11
 	"k8s.io/kubernetes/pkg/runtime"
10 12
 	"k8s.io/kubernetes/pkg/watch"
11 13
 )
... ...
@@ -337,3 +339,115 @@ func (f *namespaceInformer) Lister() *cache.IndexerToNamespaceLister {
337 337
 	informer := f.Informer()
338 338
 	return &cache.IndexerToNamespaceLister{Indexer: informer.GetIndexer()}
339 339
 }
340
+
341
+type LimitRangeInformer interface {
342
+	Informer() framework.SharedIndexInformer
343
+	Indexer() cache.Indexer
344
+	Lister() StoreToLimitRangeLister
345
+}
346
+
347
+type limitRangeInformer struct {
348
+	*sharedInformerFactory
349
+}
350
+
351
+func (f *limitRangeInformer) Informer() framework.SharedIndexInformer {
352
+	f.lock.Lock()
353
+	defer f.lock.Unlock()
354
+
355
+	informerObj := &kapi.LimitRange{}
356
+	informerType := reflect.TypeOf(informerObj)
357
+	informer, exists := f.informers[informerType]
358
+	if exists {
359
+		return informer
360
+	}
361
+
362
+	lw := f.customListerWatchers.GetListerWatcher(kapi.Resource("limitrange"))
363
+	if lw == nil {
364
+		lw = &cache.ListWatch{
365
+			ListFunc: func(options kapi.ListOptions) (runtime.Object, error) {
366
+				return f.kubeClient.LimitRanges(kapi.NamespaceAll).List(options)
367
+			},
368
+			WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) {
369
+				return f.kubeClient.LimitRanges(kapi.NamespaceAll).Watch(options)
370
+			},
371
+		}
372
+	}
373
+
374
+	informer = framework.NewSharedIndexInformer(
375
+		lw,
376
+		informerObj,
377
+		f.defaultResync,
378
+		cache.Indexers{},
379
+	)
380
+	f.informers[informerType] = informer
381
+
382
+	return informer
383
+}
384
+
385
+func (f *limitRangeInformer) Indexer() cache.Indexer {
386
+	informer := f.Informer()
387
+	return informer.GetIndexer()
388
+}
389
+
390
+func (f *limitRangeInformer) Lister() StoreToLimitRangeLister {
391
+	informer := f.Informer()
392
+	return StoreToLimitRangeLister{Indexer: informer.GetIndexer()}
393
+}
394
+
395
+// StoreToLimitRangeLister gives a store List and Get methods. The store must contain only LimitRanges.
396
+type StoreToLimitRangeLister struct {
397
+	cache.Indexer
398
+}
399
+
400
+func (s StoreToLimitRangeLister) LimitRanges(namespace string) storeLimitRangesNamespacer {
401
+	return storeLimitRangesNamespacer{s.Indexer, namespace}
402
+}
403
+
404
+type storeLimitRangesNamespacer struct {
405
+	indexer   cache.Indexer
406
+	namespace string
407
+}
408
+
409
+func (s storeLimitRangesNamespacer) List(selector labels.Selector) ([]*kapi.LimitRange, error) {
410
+	var controllers []*kapi.LimitRange
411
+
412
+	if s.namespace == kapi.NamespaceAll {
413
+		for _, m := range s.indexer.List() {
414
+			rc := m.(*kapi.LimitRange)
415
+			if selector.Matches(labels.Set(rc.Labels)) {
416
+				controllers = append(controllers, rc)
417
+			}
418
+		}
419
+		return controllers, nil
420
+	}
421
+
422
+	key := &kapi.LimitRange{ObjectMeta: kapi.ObjectMeta{Namespace: s.namespace}}
423
+	items, err := s.indexer.Index(cache.NamespaceIndex, key)
424
+	if err != nil {
425
+		for _, m := range s.indexer.List() {
426
+			rc := m.(*kapi.LimitRange)
427
+			if s.namespace == rc.Namespace && selector.Matches(labels.Set(rc.Labels)) {
428
+				controllers = append(controllers, rc)
429
+			}
430
+		}
431
+		return controllers, nil
432
+	}
433
+	for _, m := range items {
434
+		rc := m.(*kapi.LimitRange)
435
+		if selector.Matches(labels.Set(rc.Labels)) {
436
+			controllers = append(controllers, rc)
437
+		}
438
+	}
439
+	return controllers, nil
440
+}
441
+
442
+func (s storeLimitRangesNamespacer) Get(name string) (*kapi.LimitRange, error) {
443
+	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
444
+	if err != nil {
445
+		return nil, err
446
+	}
447
+	if !exists {
448
+		return nil, errors.NewNotFound(kapi.Resource("limitrange"), name)
449
+	}
450
+	return obj.(*kapi.LimitRange), nil
451
+}
... ...
@@ -26,6 +26,7 @@ type InformerFactory interface {
26 26
 	PersistentVolumes() PersistentVolumeInformer
27 27
 	PersistentVolumeClaims() PersistentVolumeClaimInformer
28 28
 	ReplicationControllers() ReplicationControllerInformer
29
+	LimitRanges() LimitRangeInformer
29 30
 
30 31
 	ClusterPolicies() ClusterPolicyInformer
31 32
 	ClusterPolicyBindings() ClusterPolicyBindingInformer
... ...
@@ -129,6 +130,10 @@ func (f *sharedInformerFactory) Namespaces() NamespaceInformer {
129 129
 	return &namespaceInformer{sharedInformerFactory: f}
130 130
 }
131 131
 
132
+func (f *sharedInformerFactory) LimitRanges() LimitRangeInformer {
133
+	return &limitRangeInformer{sharedInformerFactory: f}
134
+}
135
+
132 136
 func (f *sharedInformerFactory) ClusterPolicies() ClusterPolicyInformer {
133 137
 	return &clusterPolicyInformer{sharedInformerFactory: f}
134 138
 }
... ...
@@ -1,14 +1,9 @@
1 1
 package admission
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	kapi "k8s.io/kubernetes/pkg/api"
6 5
 	kapierrors "k8s.io/kubernetes/pkg/api/errors"
7
-	"k8s.io/kubernetes/pkg/client/cache"
8
-	kclient "k8s.io/kubernetes/pkg/client/unversioned"
9
-	"k8s.io/kubernetes/pkg/runtime"
10 6
 	kerrutil "k8s.io/kubernetes/pkg/util/errors"
11
-	watch "k8s.io/kubernetes/pkg/watch"
12 7
 
13 8
 	imageapi "github.com/openshift/origin/pkg/image/api"
14 9
 )
... ...
@@ -17,79 +12,80 @@ type LimitVerifier interface {
17 17
 	VerifyLimits(namespace string, is *imageapi.ImageStream) error
18 18
 }
19 19
 
20
-func NewLimitVerifier(client kclient.LimitRangesNamespacer) LimitVerifier {
21
-	lw := &cache.ListWatch{
22
-		ListFunc: func(options kapi.ListOptions) (runtime.Object, error) {
23
-			return client.LimitRanges(kapi.NamespaceAll).List(options)
24
-		},
25
-		WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) {
26
-			return client.LimitRanges(kapi.NamespaceAll).Watch(options)
27
-		},
28
-	}
29
-	indexer, reflector := cache.NewNamespaceKeyedIndexerAndReflector(lw, &kapi.LimitRange{}, 0)
30
-	reflector.Run()
20
+type NamespaceLimiter interface {
21
+	LimitsForNamespace(namespace string) (kapi.ResourceList, error)
22
+}
23
+
24
+// NewLimitVerifier accepts a NamespaceLimiter
25
+func NewLimitVerifier(limiter NamespaceLimiter) LimitVerifier {
31 26
 	return &limitVerifier{
32
-		client:  client,
33
-		indexer: indexer,
27
+		limiter: limiter,
34 28
 	}
35 29
 }
36 30
 
37 31
 type limitVerifier struct {
38
-	client  kclient.LimitRangesNamespacer
39
-	indexer cache.Indexer
32
+	limiter NamespaceLimiter
40 33
 }
41 34
 
42 35
 func (v *limitVerifier) VerifyLimits(namespace string, is *imageapi.ImageStream) error {
43
-	items, err := v.indexer.Index("namespace", &kapi.LimitRange{ObjectMeta: kapi.ObjectMeta{Namespace: namespace}})
44
-	if err != nil {
45
-		return fmt.Errorf("error resolving limit ranges: %v", err)
46
-	}
47
-	if len(items) == 0 {
48
-		return nil
49
-	}
50
-	limits := getMaxLimits(items)
51
-	if len(limits) == 0 {
52
-		return nil
36
+	limits, err := v.limiter.LimitsForNamespace(namespace)
37
+	if err != nil || len(limits) == 0 {
38
+		return err
53 39
 	}
54 40
 
55 41
 	usage := GetImageStreamUsage(is)
56
-
57 42
 	if err := verifyImageStreamUsage(usage, limits); err != nil {
58 43
 		return kapierrors.NewForbidden(imageapi.Resource("ImageStream"), is.Name, err)
59 44
 	}
60 45
 	return nil
61 46
 }
62 47
 
63
-func getMaxLimits(limits []interface{}) kapi.ResourceList {
64
-	res := kapi.ResourceList{}
48
+func verifyImageStreamUsage(isUsage kapi.ResourceList, limits kapi.ResourceList) error {
49
+	var errs []error
65 50
 
66
-	for _, limitObject := range limits {
67
-		lr := limitObject.(*kapi.LimitRange)
68
-		for _, item := range lr.Spec.Limits {
69
-			if item.Type != imageapi.LimitTypeImageStream {
70
-				continue
71
-			}
72
-			for _, resource := range []kapi.ResourceName{imageapi.ResourceImageStreamImages, imageapi.ResourceImageStreamTags} {
73
-				if max, ok := item.Max[resource]; ok {
74
-					if oldMax, exists := res[resource]; !exists || oldMax.Cmp(max) > 0 {
75
-						res[resource] = max
76
-					}
77
-				}
78
-			}
51
+	for resource, limit := range limits {
52
+		if usage, ok := isUsage[resource]; ok && usage.Cmp(limit) > 0 {
53
+			errs = append(errs, newLimitExceededError(imageapi.LimitTypeImageStream, resource, &usage, &limit))
79 54
 		}
80 55
 	}
81 56
 
82
-	return res
57
+	return kerrutil.NewAggregate(errs)
83 58
 }
84 59
 
85
-func verifyImageStreamUsage(isUsage kapi.ResourceList, limits kapi.ResourceList) error {
86
-	errs := []error{}
60
+type LimitRangesForNamespaceFunc func(namespace string) ([]*kapi.LimitRange, error)
87 61
 
88
-	for resource, limit := range limits {
89
-		if usage, ok := isUsage[resource]; ok && usage.Cmp(limit) > 0 {
90
-			errs = append(errs, newLimitExceededError(imageapi.LimitTypeImageStream, resource, &usage, &limit))
62
+func (fn LimitRangesForNamespaceFunc) LimitsForNamespace(namespace string) (kapi.ResourceList, error) {
63
+	items, err := fn(namespace)
64
+	if err != nil {
65
+		return nil, err
66
+	}
67
+	var res kapi.ResourceList
68
+	for _, limitRange := range items {
69
+		res = getMaxLimits(limitRange, res)
70
+	}
71
+	return res, nil
72
+}
73
+
74
+// getMaxLimits updates the resource list to include the max allowed image count
75
+// TODO: use the existing Max function for resource lists.
76
+func getMaxLimits(limit *kapi.LimitRange, current kapi.ResourceList) kapi.ResourceList {
77
+	res := current
78
+
79
+	for _, item := range limit.Spec.Limits {
80
+		if item.Type != imageapi.LimitTypeImageStream {
81
+			continue
82
+		}
83
+		for _, resource := range []kapi.ResourceName{imageapi.ResourceImageStreamImages, imageapi.ResourceImageStreamTags} {
84
+			if max, ok := item.Max[resource]; ok {
85
+				if oldMax, exists := res[resource]; !exists || oldMax.Cmp(max) > 0 {
86
+					if res == nil {
87
+						res = make(kapi.ResourceList)
88
+					}
89
+					res[resource] = max
90
+				}
91
+			}
91 92
 		}
92 93
 	}
93 94
 
94
-	return kerrutil.NewAggregate(errs)
95
+	return res
95 96
 }
... ...
@@ -6,8 +6,6 @@ import (
6 6
 
7 7
 	kapi "k8s.io/kubernetes/pkg/api"
8 8
 	"k8s.io/kubernetes/pkg/api/resource"
9
-	"k8s.io/kubernetes/pkg/client/cache"
10
-	fake "k8s.io/kubernetes/pkg/client/unversioned/testclient"
11 9
 
12 10
 	imagetest "github.com/openshift/origin/pkg/image/admission/testutil"
13 11
 	imageapi "github.com/openshift/origin/pkg/image/api"
... ...
@@ -214,11 +212,10 @@ func TestGetMaxLimits(t *testing.T) {
214 214
 			},
215 215
 		},
216 216
 	} {
217
-		lrs := make([]interface{}, len(tc.lrs))
217
+		var limits kapi.ResourceList
218 218
 		for i := range tc.lrs {
219
-			lrs[i] = &tc.lrs[i]
219
+			limits = getMaxLimits(&tc.lrs[i], limits)
220 220
 		}
221
-		limits := getMaxLimits(lrs)
222 221
 		if len(limits) != len(tc.expectedLimits) {
223 222
 			t.Errorf("[%s] got unexpected number of limits (%d != %d)", tc.name, len(limits), len(tc.expectedLimits))
224 223
 		}
... ...
@@ -390,13 +387,10 @@ func TestVerifyLimits(t *testing.T) {
390 390
 			},
391 391
 		}
392 392
 
393
-		kubeClient := fake.NewSimpleFake(limitRange)
394
-		indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc})
395
-		indexer.Add(limitRange)
396
-
397 393
 		verifier := &limitVerifier{
398
-			client:  kubeClient,
399
-			indexer: indexer,
394
+			limiter: LimitRangesForNamespaceFunc(func(ns string) ([]*kapi.LimitRange, error) {
395
+				return []*kapi.LimitRange{limitRange}, nil
396
+			}),
400 397
 		}
401 398
 
402 399
 		err := verifier.VerifyLimits("test", &tc.is)
... ...
@@ -43,9 +43,12 @@ func NewREST(optsGetter restoptions.Getter, defaultRegistry api.DefaultRegistry,
43 43
 		ReturnDeletedObject: false,
44 44
 	}
45 45
 
46
-	strategy := imagestream.NewStrategy(defaultRegistry, subjectAccessReviewRegistry, limitVerifier)
47
-	rest := &REST{Store: &store, subjectAccessReviewRegistry: subjectAccessReviewRegistry}
48
-	strategy.ImageStreamGetter = rest
46
+	rest := &REST{
47
+		Store: &store,
48
+		subjectAccessReviewRegistry: subjectAccessReviewRegistry,
49
+	}
50
+	// strategy must be able to load image streams across namespaces during tag verification
51
+	strategy := imagestream.NewStrategy(defaultRegistry, subjectAccessReviewRegistry, limitVerifier, rest)
49 52
 
50 53
 	store.CreateStrategy = strategy
51 54
 	store.UpdateStrategy = strategy
... ...
@@ -55,10 +58,12 @@ func NewREST(optsGetter restoptions.Getter, defaultRegistry api.DefaultRegistry,
55 55
 		return nil, nil, nil, err
56 56
 	}
57 57
 
58
+	statusStrategy := imagestream.NewStatusStrategy(strategy)
58 59
 	statusStore := store
59 60
 	statusStore.Decorator = nil
60 61
 	statusStore.CreateStrategy = nil
61
-	statusStore.UpdateStrategy = imagestream.NewStatusStrategy(strategy)
62
+	statusStore.UpdateStrategy = statusStrategy
63
+	statusREST := &StatusREST{store: &statusStore}
62 64
 
63 65
 	internalStore := store
64 66
 	internalStrategy := imagestream.NewInternalStrategy(strategy)
... ...
@@ -66,7 +71,8 @@ func NewREST(optsGetter restoptions.Getter, defaultRegistry api.DefaultRegistry,
66 66
 	internalStore.CreateStrategy = internalStrategy
67 67
 	internalStore.UpdateStrategy = internalStrategy
68 68
 
69
-	return rest, &StatusREST{store: &statusStore}, &InternalREST{store: &internalStore}, nil
69
+	internalREST := &InternalREST{store: &internalStore}
70
+	return rest, statusREST, internalREST, nil
70 71
 }
71 72
 
72 73
 // StatusREST implements the REST endpoint for changing the status of an image stream.
... ...
@@ -119,68 +119,6 @@ func TestGetImageStreamOK(t *testing.T) {
119 119
 	}
120 120
 }
121 121
 
122
-// func TestListImageStreamsError(t *testing.T) {
123
-// 	storage, _, _, server := newStorage(t)
124
-// 	defer server.Terminate(t)
125
-
126
-// 	objs, err := storage.List(kapi.NewDefaultContext(), nil)
127
-// 	if err != nil {
128
-// 		t.Errorf("Expected err, got nothing")
129
-// 	}
130
-// 	got := objs.(*api.ImageStreamList)
131
-// 	if got == nil || len(got.Items) != 0 {
132
-// 		t.Errorf("Unexpected empty imageStreams list, got %#v", got)
133
-// 	}
134
-// }
135
-
136
-// func TestListImageStreamsEmptyList(t *testing.T) {
137
-// 	fakeEtcdClient, helper := newStorage(t)
138
-// 	fakeEtcdClient.ChangeIndex = 1
139
-// 	fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default")] = tools.EtcdResponseWithError{
140
-// 		R: &etcd.Response{},
141
-// 		E: fakeEtcdClient.NewError(tools.EtcdErrorCodeNotFound),
142
-// 	}
143
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
144
-
145
-// 	imageStreams, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything())
146
-// 	if err != nil {
147
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
148
-// 	}
149
-// 	if len(imageStreams.(*api.ImageStreamList).Items) != 0 {
150
-// 		t.Errorf("Unexpected non-zero imageStreams list: %#v", imageStreams)
151
-// 	}
152
-// 	if imageStreams.(*api.ImageStreamList).ResourceVersion != "1" {
153
-// 		t.Errorf("Unexpected resource version: %#v", imageStreams)
154
-// 	}
155
-// }
156
-
157
-// func TestListImageStreamsPopulatedList(t *testing.T) {
158
-// 	fakeEtcdClient, helper := newStorage(t)
159
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
160
-
161
-// 	fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default")] = tools.EtcdResponseWithError{
162
-// 		R: &etcd.Response{
163
-// 			Node: &etcd.Node{
164
-// 				Nodes: []*etcd.Node{
165
-// 					{Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}})},
166
-// 					{Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}})},
167
-// 				},
168
-// 			},
169
-// 		},
170
-// 	}
171
-
172
-// 	list, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything())
173
-// 	if err != nil {
174
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
175
-// 	}
176
-
177
-// 	imageStreams := list.(*api.ImageStreamList)
178
-
179
-// 	if e, a := 2, len(imageStreams.Items); e != a {
180
-// 		t.Errorf("Expected %v, got %v", e, a)
181
-// 	}
182
-// }
183
-
184 122
 type fakeUser struct {
185 123
 }
186 124
 
... ...
@@ -201,1021 +139,3 @@ func (u *fakeUser) GetGroups() []string {
201 201
 func (u *fakeUser) GetExtra() map[string][]string {
202 202
 	return map[string][]string{}
203 203
 }
204
-
205
-// func TestCreateImageStreamOK(t *testing.T) {
206
-// 	_, helper := newStorage(t)
207
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
208
-
209
-// 	stream := &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}
210
-// 	ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
211
-// 	_, err := storage.Create(ctx, stream)
212
-// 	if err != nil {
213
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
214
-// 	}
215
-
216
-// 	actual := &api.ImageStream{}
217
-// 	if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil {
218
-// 		t.Fatalf("unexpected extraction error: %v", err)
219
-// 	}
220
-// 	if actual.Name != stream.Name {
221
-// 		t.Errorf("unexpected stream: %#v", actual)
222
-// 	}
223
-// 	if len(actual.UID) == 0 {
224
-// 		t.Errorf("expected stream UID to be set: %#v", actual)
225
-// 	}
226
-// 	if stream.CreationTimestamp.IsZero() {
227
-// 		t.Error("Unexpected zero CreationTimestamp")
228
-// 	}
229
-// 	if stream.Spec.DockerImageRepository != "" {
230
-// 		t.Errorf("unexpected stream: %#v", stream)
231
-// 	}
232
-// }
233
-
234
-// func TestCreateImageStreamSpecTagsFromSet(t *testing.T) {
235
-// 	tests := map[string]struct {
236
-// 		otherNamespace string
237
-// 		sarExpected    bool
238
-// 		sarAllowed     bool
239
-// 	}{
240
-// 		"same namespace (blank), no sar": {
241
-// 			otherNamespace: "",
242
-// 			sarExpected:    false,
243
-// 		},
244
-// 		"same namespace (set), no sar": {
245
-// 			otherNamespace: "default",
246
-// 			sarExpected:    false,
247
-// 		},
248
-// 		"different namespace, sar allowed": {
249
-// 			otherNamespace: "otherns",
250
-// 			sarExpected:    true,
251
-// 			sarAllowed:     true,
252
-// 		},
253
-// 		"different namespace, sar denied": {
254
-// 			otherNamespace: "otherns",
255
-// 			sarExpected:    true,
256
-// 			sarAllowed:     false,
257
-// 		},
258
-// 	}
259
-// 	for name, test := range tests {
260
-// 		fakeEtcdClient, helper := newStorage(t)
261
-// 		sarRegistry := &fakeSubjectAccessReviewRegistry{
262
-// 			allow: test.sarAllowed,
263
-// 		}
264
-// 		storage, _, _ := NewREST(helper, noDefaultRegistry, sarRegistry)
265
-
266
-// 		otherNamespace := test.otherNamespace
267
-// 		if len(otherNamespace) == 0 {
268
-// 			otherNamespace = "default"
269
-// 		}
270
-// 		fakeEtcdClient.Data[fmt.Sprintf(etcdtest.AddPrefix("/imagestreams/%s/other"), otherNamespace)] = tools.EtcdResponseWithError{
271
-// 			R: &etcd.Response{
272
-// 				Node: &etcd.Node{
273
-// 					Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
274
-// 						ObjectMeta: kapi.ObjectMeta{Name: "other", Namespace: otherNamespace},
275
-// 						Status: api.ImageStreamStatus{
276
-// 							Tags: map[string]api.TagEventList{
277
-// 								"latest": {
278
-// 									Items: []api.TagEvent{
279
-// 										{
280
-// 											DockerImageReference: fmt.Sprintf("%s/other:latest", otherNamespace),
281
-// 										},
282
-// 									},
283
-// 								},
284
-// 							},
285
-// 						},
286
-// 					}),
287
-// 					ModifiedIndex: 1,
288
-// 				},
289
-// 			},
290
-// 		}
291
-
292
-// 		stream := &api.ImageStream{
293
-// 			ObjectMeta: kapi.ObjectMeta{Name: "foo"},
294
-// 			Spec: api.ImageStreamSpec{
295
-// 				Tags: map[string]api.TagReference{
296
-// 					"other": {
297
-// 						From: &kapi.ObjectReference{
298
-// 							Kind:      "ImageStreamTag",
299
-// 							Namespace: test.otherNamespace,
300
-// 							Name:      "other:latest",
301
-// 						},
302
-// 					},
303
-// 				},
304
-// 			},
305
-// 		}
306
-// 		ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
307
-// 		_, err := storage.Create(ctx, stream)
308
-// 		if test.sarExpected {
309
-// 			if sarRegistry.request == nil {
310
-// 				t.Errorf("%s: expected sar request", name)
311
-// 				continue
312
-// 			}
313
-// 			if e, a := test.sarAllowed, err == nil; e != a {
314
-// 				t.Errorf("%s: expected sarAllowed=%t, got error %t: %v", name, e, a, err)
315
-// 				continue
316
-// 			}
317
-
318
-// 			continue
319
-// 		}
320
-
321
-// 		// sar not expected
322
-// 		if err != nil {
323
-// 			t.Fatalf("%s: unexpected error: %v", name, err)
324
-// 		}
325
-
326
-// 		actual := &api.ImageStream{}
327
-// 		if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil {
328
-// 			t.Fatalf("%s: unexpected extraction error: %v", name, err)
329
-// 		}
330
-// 		if e, a := fmt.Sprintf("%s/other:latest", otherNamespace), actual.Status.Tags["other"].Items[0].DockerImageReference; e != a {
331
-// 			t.Errorf("%s: dockerImageReference: expected %q, got %q", name, e, a)
332
-// 		}
333
-// 	}
334
-// }
335
-
336
-// func TestCreateRegistryErrorSaving(t *testing.T) {
337
-// 	fakeEtcdClient, helper := newStorage(t)
338
-// 	fakeEtcdClient.Err = fmt.Errorf("foo")
339
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
340
-
341
-// 	ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
342
-// 	_, err := storage.Create(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}})
343
-// 	if err != fakeEtcdClient.Err {
344
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
345
-// 	}
346
-// }
347
-
348
-// func TestUpdateImageStreamMissingID(t *testing.T) {
349
-// 	_, helper := newStorage(t)
350
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
351
-
352
-// 	obj, created, err := storage.Update(kapi.NewDefaultContext(), &api.ImageStream{})
353
-// 	if obj != nil || created {
354
-// 		t.Fatalf("Expected nil, got %v", obj)
355
-// 	}
356
-// 	if strings.Index(err.Error(), "Name parameter required") == -1 {
357
-// 		t.Errorf("Expected 'Name parameter required' error, got %v", err)
358
-// 	}
359
-// }
360
-
361
-// func TestUpdateRegistryErrorSaving(t *testing.T) {
362
-// 	fakeEtcdClient, helper := newStorage(t)
363
-// 	fakeEtcdClient.Err = fmt.Errorf("foo")
364
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
365
-
366
-// 	_, created, err := storage.Update(kapi.NewDefaultContext(), &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}})
367
-// 	if err != fakeEtcdClient.Err || created {
368
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
369
-// 	}
370
-// }
371
-
372
-// func TestUpdateImageStreamOK(t *testing.T) {
373
-// 	fakeEtcdClient, helper := newStorage(t)
374
-// 	fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/bar")] = tools.EtcdResponseWithError{
375
-// 		R: &etcd.Response{
376
-// 			Node: &etcd.Node{
377
-// 				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
378
-// 					ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "default"},
379
-// 				}),
380
-// 				ModifiedIndex: 2,
381
-// 			},
382
-// 		},
383
-// 	}
384
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
385
-
386
-// 	ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
387
-// 	obj, created, err := storage.Update(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar", ResourceVersion: "1"}})
388
-// 	if !errors.IsConflict(err) {
389
-// 		t.Fatalf("unexpected non-error: %v", err)
390
-// 	}
391
-// 	obj, created, err = storage.Update(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar", ResourceVersion: "2"}})
392
-// 	if err != nil || created {
393
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
394
-// 	}
395
-// 	stream, ok := obj.(*api.ImageStream)
396
-// 	if !ok {
397
-// 		t.Errorf("Expected image stream, got %#v", obj)
398
-// 	}
399
-// 	if stream.Name != "bar" {
400
-// 		t.Errorf("Unexpected stream returned: %#v", stream)
401
-// 	}
402
-// }
403
-
404
-// func TestUpdateImageStreamSpecTagsFromSet(t *testing.T) {
405
-// 	tests := map[string]struct {
406
-// 		otherNamespace string
407
-// 		sarExpected    bool
408
-// 		sarAllowed     bool
409
-// 	}{
410
-// 		"same namespace (blank), no sar": {
411
-// 			otherNamespace: "",
412
-// 			sarExpected:    false,
413
-// 		},
414
-// 		"same namespace (set), no sar": {
415
-// 			otherNamespace: "default",
416
-// 			sarExpected:    false,
417
-// 		},
418
-// 		"different namespace, sar allowed": {
419
-// 			otherNamespace: "otherns",
420
-// 			sarExpected:    true,
421
-// 			sarAllowed:     true,
422
-// 		},
423
-// 		"different namespace, sar denied": {
424
-// 			otherNamespace: "otherns",
425
-// 			sarExpected:    true,
426
-// 			sarAllowed:     false,
427
-// 		},
428
-// 	}
429
-// 	for name, test := range tests {
430
-// 		fakeEtcdClient, helper := newStorage(t)
431
-// 		sarRegistry := &fakeSubjectAccessReviewRegistry{
432
-// 			allow: test.sarAllowed,
433
-// 		}
434
-// 		storage, _, _ := NewREST(helper, noDefaultRegistry, sarRegistry)
435
-
436
-// 		fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/foo")] = tools.EtcdResponseWithError{
437
-// 			R: &etcd.Response{
438
-// 				Node: &etcd.Node{
439
-// 					Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
440
-// 						ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "default"},
441
-// 					}),
442
-// 					ModifiedIndex: 1,
443
-// 				},
444
-// 			},
445
-// 		}
446
-
447
-// 		otherNamespace := test.otherNamespace
448
-// 		if len(otherNamespace) == 0 {
449
-// 			otherNamespace = "default"
450
-// 		}
451
-// 		fakeEtcdClient.Data[fmt.Sprintf(etcdtest.AddPrefix("/imagestreams/%s/other"), otherNamespace)] = tools.EtcdResponseWithError{
452
-// 			R: &etcd.Response{
453
-// 				Node: &etcd.Node{
454
-// 					Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
455
-// 						ObjectMeta: kapi.ObjectMeta{Name: "other", Namespace: otherNamespace},
456
-// 						Status: api.ImageStreamStatus{
457
-// 							Tags: map[string]api.TagEventList{
458
-// 								"latest": {
459
-// 									Items: []api.TagEvent{
460
-// 										{
461
-// 											DockerImageReference: fmt.Sprintf("%s/other:latest", otherNamespace),
462
-// 										},
463
-// 									},
464
-// 								},
465
-// 							},
466
-// 						},
467
-// 					}),
468
-// 					ModifiedIndex: 1,
469
-// 				},
470
-// 			},
471
-// 		}
472
-
473
-// 		stream := &api.ImageStream{
474
-// 			ObjectMeta: kapi.ObjectMeta{Name: "foo", ResourceVersion: "1"},
475
-// 			Spec: api.ImageStreamSpec{
476
-// 				Tags: map[string]api.TagReference{
477
-// 					"other": {
478
-// 						From: &kapi.ObjectReference{
479
-// 							Kind:      "ImageStreamTag",
480
-// 							Namespace: test.otherNamespace,
481
-// 							Name:      "other:latest",
482
-// 						},
483
-// 					},
484
-// 				},
485
-// 			},
486
-// 		}
487
-// 		ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
488
-// 		_, _, err := storage.Update(ctx, stream)
489
-// 		if test.sarExpected {
490
-// 			if sarRegistry.request == nil {
491
-// 				t.Errorf("%s: expected sar request", name)
492
-// 				continue
493
-// 			}
494
-// 			if e, a := test.sarAllowed, err == nil; e != a {
495
-// 				t.Errorf("%s: expected sarAllowed=%t, got error %t: %v", name, e, a, err)
496
-// 				continue
497
-// 			}
498
-
499
-// 			continue
500
-// 		}
501
-
502
-// 		// sar not expected
503
-// 		if err != nil {
504
-// 			t.Fatalf("%s: unexpected error: %v", name, err)
505
-// 		}
506
-
507
-// 		actual := &api.ImageStream{}
508
-// 		if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil {
509
-// 			t.Fatalf("%s: unexpected extraction error: %v", name, err)
510
-// 		}
511
-// 		if e, a := fmt.Sprintf("%s/other:latest", otherNamespace), actual.Status.Tags["other"].Items[0].DockerImageReference; e != a {
512
-// 			t.Errorf("%s: dockerImageReference: expected %q, got %q", name, e, a)
513
-// 		}
514
-// 	}
515
-// }
516
-
517
-// func TestUpdateImageStreamTags(t *testing.T) {
518
-// 	fakeEtcdClient, helper := newStorage(t)
519
-// 	fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/test")] = tools.EtcdResponseWithError{
520
-// 		R: &etcd.Response{
521
-// 			Node: &etcd.Node{
522
-// 				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
523
-// 					ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "default"},
524
-// 					Spec: api.ImageStreamSpec{
525
-// 						Tags: map[string]api.TagReference{
526
-// 							"another": {
527
-// 								From: &kapi.ObjectReference{
528
-// 									Kind: "ImageStreamTag",
529
-// 									Name: "test:another",
530
-// 								},
531
-// 							},
532
-// 							api.DefaultImageTag: {
533
-// 								From: &kapi.ObjectReference{
534
-// 									Kind: "ImageStreamTag",
535
-// 									Name: "test:latest",
536
-// 								},
537
-// 							},
538
-// 						},
539
-// 					},
540
-// 					Status: api.ImageStreamStatus{
541
-// 						DockerImageRepository: "registry.default.local/default/test",
542
-// 						Tags: map[string]api.TagEventList{
543
-// 							api.DefaultImageTag: {
544
-// 								Items: []api.TagEvent{
545
-// 									{
546
-// 										DockerImageReference: "registry.default.local/default/test@sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f",
547
-// 										Image:                "sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f",
548
-// 									},
549
-// 								},
550
-// 							},
551
-// 						},
552
-// 					},
553
-// 				}),
554
-// 				ModifiedIndex: 1,
555
-// 			},
556
-// 		},
557
-// 	}
558
-
559
-// 	_, _, storage := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
560
-
561
-// 	stream := &api.ImageStream{
562
-// 		ObjectMeta: kapi.ObjectMeta{
563
-// 			Namespace:       "default",
564
-// 			Name:            "test",
565
-// 			ResourceVersion: "1",
566
-// 		},
567
-// 		Spec: api.ImageStreamSpec{
568
-// 			Tags: map[string]api.TagReference{
569
-// 				"another": {
570
-// 					From: &kapi.ObjectReference{
571
-// 						Kind: "ImageStreamTag",
572
-// 						Name: "test:another",
573
-// 					},
574
-// 				},
575
-// 				api.DefaultImageTag: {
576
-// 					From: &kapi.ObjectReference{
577
-// 						Kind: "ImageStreamTag",
578
-// 						Name: "test:latest",
579
-// 					},
580
-// 				},
581
-// 			},
582
-// 		},
583
-// 		Status: api.ImageStreamStatus{
584
-// 			DockerImageRepository: "registry.default.local/default/test",
585
-// 			Tags: map[string]api.TagEventList{
586
-// 				api.DefaultImageTag: {
587
-// 					Items: []api.TagEvent{
588
-// 						{
589
-// 							DockerImageReference: "registry.default.local/default/test@sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f",
590
-// 							Image:                "sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f",
591
-// 						},
592
-// 					},
593
-// 				},
594
-// 			},
595
-// 		},
596
-// 	}
597
-
598
-// 	delete(stream.Spec.Tags, api.DefaultImageTag)
599
-// 	delete(stream.Status.Tags, api.DefaultImageTag)
600
-
601
-// 	ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
602
-
603
-// 	obj, created, err := storage.Update(ctx, stream)
604
-// 	if err != nil {
605
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
606
-// 	}
607
-// 	if created {
608
-// 		t.Fatal("Unexpected stream creation")
609
-// 	}
610
-// 	updated, ok := obj.(*api.ImageStream)
611
-// 	if !ok {
612
-// 		t.Errorf("Expected image stream, got %#v", obj)
613
-// 	}
614
-// 	if _, ok := updated.Spec.Tags[api.DefaultImageTag]; ok {
615
-// 		t.Errorf("Expected deleted spec tag: %s", api.DefaultImageTag)
616
-// 	}
617
-// 	if _, ok := updated.Status.Tags[api.DefaultImageTag]; ok {
618
-// 		t.Errorf("Expected deleted status tag: %s", api.DefaultImageTag)
619
-// 	}
620
-// }
621
-
622
-// func TestDeleteImageStream(t *testing.T) {
623
-// 	fakeEtcdClient, helper := newStorage(t)
624
-// 	fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/foo")] = tools.EtcdResponseWithError{
625
-// 		R: &etcd.Response{
626
-// 			Node: &etcd.Node{
627
-// 				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
628
-// 					ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "default"},
629
-// 				}),
630
-// 				ModifiedIndex: 2,
631
-// 			},
632
-// 		},
633
-// 	}
634
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
635
-
636
-// 	obj, err := storage.Delete(kapi.NewDefaultContext(), "foo", nil)
637
-// 	if err != nil {
638
-// 		t.Fatalf("Unexpected non-nil error: %#v", err)
639
-// 	}
640
-// 	status, ok := obj.(*unversioned.Status)
641
-// 	if !ok {
642
-// 		t.Fatalf("Expected status, got %#v", obj)
643
-// 	}
644
-// 	if status.Status != unversioned.StatusSuccess {
645
-// 		t.Errorf("Expected status=success, got %#v", status)
646
-// 	}
647
-// }
648
-
649
-// func TestUpdateImageStreamConflictingNamespace(t *testing.T) {
650
-// 	fakeEtcdClient, helper := newStorage(t)
651
-// 	fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/legal-name/bar")] = tools.EtcdResponseWithError{
652
-// 		R: &etcd.Response{
653
-// 			Node: &etcd.Node{
654
-// 				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
655
-// 					ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "default"},
656
-// 				}),
657
-// 				ModifiedIndex: 2,
658
-// 			},
659
-// 		},
660
-// 	}
661
-// 	storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
662
-
663
-// 	ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "legal-name"), &fakeUser{})
664
-// 	obj, created, err := storage.Update(ctx, &api.ImageStream{
665
-// 		ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "some-value", ResourceVersion: "2"},
666
-// 	})
667
-
668
-// 	if obj != nil || created {
669
-// 		t.Error("Expected a nil obj, but we got a value")
670
-// 	}
671
-
672
-// 	checkExpectedNamespaceError(t, err)
673
-// }
674
-
675
-// func checkExpectedNamespaceError(t *testing.T, err error) {
676
-// 	expectedError := "the namespace of the provided object does not match the namespace sent on the request"
677
-// 	if err == nil {
678
-// 		t.Fatalf("Expected '" + expectedError + "', but we didn't get one")
679
-// 	}
680
-// 	if !strings.Contains(err.Error(), expectedError) {
681
-// 		t.Errorf("Expected '"+expectedError+"' error, got '%v'", err.Error())
682
-// 	}
683
-
684
-// }
685
-
686
-// /*
687
-// func TestEtcdListImagesStreamsEmpty(t *testing.T) {
688
-// 	fakeClient := tools.NewFakeEtcdClient(t)
689
-// 	key := makeTestDefaultImageStreamsListKey()
690
-// 	fakeClient.Data[key] = tools.EtcdResponseWithError{
691
-// 		R: &etcd.Response{
692
-// 			Node: &etcd.Node{
693
-// 				Nodes: []*etcd.Node{},
694
-// 			},
695
-// 		},
696
-// 		E: nil,
697
-// 	}
698
-// 	registry := NewTestEtcd(fakeClient)
699
-// 	repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything())
700
-// 	if err != nil {
701
-// 		t.Errorf("unexpected error: %v", err)
702
-// 	}
703
-
704
-// 	if len(repos.Items) != 0 {
705
-// 		t.Errorf("Unexpected image streams list: %#v", repos)
706
-// 	}
707
-// }
708
-
709
-// func TestEtcdListImageStreamsError(t *testing.T) {
710
-// 	fakeClient := tools.NewFakeEtcdClient(t)
711
-// 	key := makeTestDefaultImageStreamsListKey()
712
-// 	fakeClient.Data[key] = tools.EtcdResponseWithError{
713
-// 		R: &etcd.Response{
714
-// 			Node: nil,
715
-// 		},
716
-// 		E: fmt.Errorf("some error"),
717
-// 	}
718
-// 	registry := NewTestEtcd(fakeClient)
719
-// 	repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything())
720
-// 	if err == nil {
721
-// 		t.Error("unexpected nil error")
722
-// 	}
723
-
724
-// 	if repos != nil {
725
-// 		t.Errorf("Unexpected non-nil repos: %#v", repos)
726
-// 	}
727
-// }
728
-
729
-// func TestEtcdListImageStreamsEverything(t *testing.T) {
730
-// 	fakeClient := tools.NewFakeEtcdClient(t)
731
-// 	key := makeTestDefaultImageStreamsListKey()
732
-// 	fakeClient.Data[key] = tools.EtcdResponseWithError{
733
-// 		R: &etcd.Response{
734
-// 			Node: &etcd.Node{
735
-// 				Nodes: []*etcd.Node{
736
-// 					{
737
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}),
738
-// 					},
739
-// 					{
740
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}}),
741
-// 					},
742
-// 				},
743
-// 			},
744
-// 		},
745
-// 		E: nil,
746
-// 	}
747
-// 	registry := NewTestEtcd(fakeClient)
748
-// 	registry.defaultRegistry = testDefaultRegistry
749
-// 	repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything())
750
-// 	if err != nil {
751
-// 		t.Errorf("unexpected error: %v", err)
752
-// 	}
753
-
754
-// 	if len(repos.Items) != 2 || repos.Items[0].Name != "foo" || repos.Items[1].Name != "bar" || repos.Items[1].Status.DockerImageRepository != "test/default/bar" {
755
-// 		t.Errorf("Unexpected images list: %#v", repos)
756
-// 	}
757
-// }
758
-
759
-// func TestEtcdListImageStreamsFiltered(t *testing.T) {
760
-// 	fakeClient := tools.NewFakeEtcdClient(t)
761
-// 	key := makeTestDefaultImageStreamsListKey()
762
-// 	fakeClient.Data[key] = tools.EtcdResponseWithError{
763
-// 		R: &etcd.Response{
764
-// 			Node: &etcd.Node{
765
-// 				Nodes: []*etcd.Node{
766
-// 					{
767
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
768
-// 							ObjectMeta: kapi.ObjectMeta{
769
-// 								Name:   "foo",
770
-// 								Labels: map[string]string{"env": "prod"},
771
-// 							},
772
-// 						}),
773
-// 					},
774
-// 					{
775
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{
776
-// 							ObjectMeta: kapi.ObjectMeta{
777
-// 								Name:   "bar",
778
-// 								Labels: map[string]string{"env": "dev"},
779
-// 							},
780
-// 						}),
781
-// 					},
782
-// 				},
783
-// 			},
784
-// 		},
785
-// 		E: nil,
786
-// 	}
787
-// 	registry := NewTestEtcd(fakeClient)
788
-// 	repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.SelectorFromSet(labels.Set{"env": "dev"}))
789
-// 	if err != nil {
790
-// 		t.Errorf("unexpected error: %v", err)
791
-// 	}
792
-
793
-// 	if len(repos.Items) != 1 || repos.Items[0].Name != "bar" {
794
-// 		t.Errorf("Unexpected repos list: %#v", repos)
795
-// 	}
796
-// }
797
-
798
-// func TestEtcdGetImageStream(t *testing.T) {
799
-// 	fakeClient := tools.NewFakeEtcdClient(t)
800
-// 	fakeClient.Set(makeTestDefaultImageStreamsKey("foo"), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0)
801
-// 	registry := NewTestEtcd(fakeClient)
802
-// 	stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo")
803
-// 	if err != nil {
804
-// 		t.Errorf("unexpected error: %v", err)
805
-// 	}
806
-
807
-// 	if stream.Name != "foo" {
808
-// 		t.Errorf("Unexpected stream: %#v", stream)
809
-// 	}
810
-// }
811
-
812
-// func TestEtcdGetImageStreamNotFound(t *testing.T) {
813
-// 	fakeClient := tools.NewFakeEtcdClient(t)
814
-// 	fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{
815
-// 		R: &etcd.Response{
816
-// 			Node: nil,
817
-// 		},
818
-// 		E: tools.EtcdErrorNotFound,
819
-// 	}
820
-// 	registry := NewTestEtcd(fakeClient)
821
-// 	stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo")
822
-// 	if err == nil {
823
-// 		t.Errorf("Unexpected non-error.")
824
-// 	}
825
-// 	if stream != nil {
826
-// 		t.Errorf("Unexpected non-nil stream: %#v", stream)
827
-// 	}
828
-// }
829
-
830
-// func TestEtcdCreateImageStream(t *testing.T) {
831
-// 	fakeClient := tools.NewFakeEtcdClient(t)
832
-// 	fakeClient.TestIndex = true
833
-// 	fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{
834
-// 		R: &etcd.Response{
835
-// 			Node: nil,
836
-// 		},
837
-// 		E: tools.EtcdErrorNotFound,
838
-// 	}
839
-// 	registry := NewTestEtcd(fakeClient)
840
-// 	err := registry.CreateImageStream(kapi.NewDefaultContext(), &api.ImageStream{
841
-// 		ObjectMeta: kapi.ObjectMeta{
842
-// 			Name:   "foo",
843
-// 			Labels: map[string]string{"a": "b"},
844
-// 		},
845
-// 		DockerImageRepository: "c/d",
846
-// 		Tags: map[string]string{"t1": "v1"},
847
-// 	})
848
-// 	if err != nil {
849
-// 		t.Fatalf("unexpected error: %v", err)
850
-// 	}
851
-
852
-// 	resp, err := fakeClient.Get(makeTestDefaultImageStreamsKey("foo"), false, false)
853
-// 	if err != nil {
854
-// 		t.Fatalf("Unexpected error %v", err)
855
-// 	}
856
-// 	var stream api.ImageStream
857
-// 	err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &stream)
858
-// 	if err != nil {
859
-// 		t.Errorf("unexpected error: %v", err)
860
-// 	}
861
-
862
-// 	if stream.Name != "foo" {
863
-// 		t.Errorf("Unexpected stream: %#v %s", stream, resp.Node.Value)
864
-// 	}
865
-
866
-// 	if len(stream.Labels) != 1 || stream.Labels["a"] != "b" {
867
-// 		t.Errorf("Unexpected labels: %#v", stream.Labels)
868
-// 	}
869
-
870
-// 	if stream.DockerImageRepository != "c/d" {
871
-// 		t.Errorf("Unexpected docker image stream: %s", stream.DockerImageRepository)
872
-// 	}
873
-
874
-// 	if len(stream.Tags) != 1 || stream.Tags["t1"] != "v1" {
875
-// 		t.Errorf("Unexpected tags: %#v", stream.Tags)
876
-// 	}
877
-// }
878
-
879
-// func TestEtcdCreateImageStreamAlreadyExists(t *testing.T) {
880
-// 	fakeClient := tools.NewFakeEtcdClient(t)
881
-// 	fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{
882
-// 		R: &etcd.Response{
883
-// 			Node: &etcd.Node{
884
-// 				Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}),
885
-// 			},
886
-// 		},
887
-// 		E: nil,
888
-// 	}
889
-// 	registry := NewTestEtcd(fakeClient)
890
-// 	err := registry.CreateImageStream(kapi.NewDefaultContext(), &api.ImageStream{
891
-// 		ObjectMeta: kapi.ObjectMeta{
892
-// 			Name: "foo",
893
-// 		},
894
-// 	})
895
-// 	if err == nil {
896
-// 		t.Error("Unexpected non-error")
897
-// 	}
898
-// 	if !errors.IsAlreadyExists(err) {
899
-// 		t.Errorf("Expected 'already exists' error, got %#v", err)
900
-// 	}
901
-// }
902
-
903
-// func TestEtcdUpdateImageStream(t *testing.T) {
904
-// 	fakeClient := tools.NewFakeEtcdClient(t)
905
-// 	fakeClient.TestIndex = true
906
-
907
-// 	resp, _ := fakeClient.Set(makeTestDefaultImageStreamsKey("foo"), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0)
908
-// 	registry := NewTestEtcd(fakeClient)
909
-// 	err := registry.UpdateImageStreamSpec(kapi.NewDefaultContext(), &api.ImageStream{
910
-// 		ObjectMeta:            kapi.ObjectMeta{Name: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)},
911
-// 		DockerImageRepository: "some/stream",
912
-// 	})
913
-// 	if err != nil {
914
-// 		t.Errorf("unexpected error: %v", err)
915
-// 	}
916
-
917
-// 	stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo")
918
-// 	if stream.DockerImageRepository != "some/stream" {
919
-// 		t.Errorf("Unexpected stream: %#v", stream)
920
-// 	}
921
-// }
922
-
923
-// func TestEtcdDeleteImageStreamNotFound(t *testing.T) {
924
-// 	fakeClient := tools.NewFakeEtcdClient(t)
925
-// 	fakeClient.Err = tools.EtcdErrorNotFound
926
-// 	registry := NewTestEtcd(fakeClient)
927
-// 	err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo")
928
-// 	if err == nil {
929
-// 		t.Error("Unexpected non-error")
930
-// 	}
931
-// 	if !errors.IsNotFound(err) {
932
-// 		t.Errorf("Expected 'not found' error, got %#v", err)
933
-// 	}
934
-// }
935
-
936
-// func TestEtcdDeleteImageStreamError(t *testing.T) {
937
-// 	fakeClient := tools.NewFakeEtcdClient(t)
938
-// 	fakeClient.Err = fmt.Errorf("Some error")
939
-// 	registry := NewTestEtcd(fakeClient)
940
-// 	err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo")
941
-// 	if err == nil {
942
-// 		t.Error("Unexpected non-error")
943
-// 	}
944
-// }
945
-
946
-// func TestEtcdDeleteImageStreamOK(t *testing.T) {
947
-// 	fakeClient := tools.NewFakeEtcdClient(t)
948
-// 	registry := NewTestEtcd(fakeClient)
949
-// 	key := makeTestDefaultImageStreamsListKey() + "/foo"
950
-// 	err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo")
951
-// 	if err != nil {
952
-// 		t.Errorf("Unexpected error: %#v", err)
953
-// 	}
954
-// 	if len(fakeClient.DeletedKeys) != 1 {
955
-// 		t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
956
-// 	} else if fakeClient.DeletedKeys[0] != key {
957
-// 		t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
958
-// 	}
959
-// }
960
-
961
-// func TestEtcdWatchImageStreams(t *testing.T) {
962
-// 	fakeClient := tools.NewFakeEtcdClient(t)
963
-// 	registry := NewTestEtcd(fakeClient)
964
-
965
-// 	var tests = []struct {
966
-// 		label    labels.Selector
967
-// 		field    labels.Selector
968
-// 		repos    []*api.ImageStream
969
-// 		expected []bool
970
-// 	}{
971
-// 		// want everything
972
-// 		{
973
-// 			labels.Everything(),
974
-// 			labels.Everything(),
975
-// 			[]*api.ImageStream{
976
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"l1": "v1"}}, DockerImageRepository: "r1"},
977
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"},
978
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"l3": "v3"}}, DockerImageRepository: "r3"},
979
-// 			},
980
-// 			[]bool{
981
-// 				true,
982
-// 				true,
983
-// 				true,
984
-// 			},
985
-// 		},
986
-// 		// want name=foo
987
-// 		{
988
-// 			labels.Everything(),
989
-// 			labels.SelectorFromSet(labels.Set{"name": "foo"}),
990
-// 			[]*api.ImageStream{
991
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"l1": "v1"}}, DockerImageRepository: "r1"},
992
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"},
993
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"l3": "v3"}}, DockerImageRepository: "r3"},
994
-// 			},
995
-// 			[]bool{
996
-// 				false,
997
-// 				true,
998
-// 				false,
999
-// 			},
1000
-// 		},
1001
-// 		// want label color:blue
1002
-// 		{
1003
-// 			labels.SelectorFromSet(labels.Set{"color": "blue"}),
1004
-// 			labels.Everything(),
1005
-// 			[]*api.ImageStream{
1006
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r1"},
1007
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"},
1008
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r3"},
1009
-// 			},
1010
-// 			[]bool{
1011
-// 				true,
1012
-// 				false,
1013
-// 				true,
1014
-// 			},
1015
-// 		},
1016
-// 		// want name=foo, label color:blue, dockerImageStream=r1
1017
-// 		{
1018
-// 			labels.SelectorFromSet(labels.Set{"color": "blue"}),
1019
-// 			labels.SelectorFromSet(labels.Set{"dockerImageStream": "r1", "name": "foo"}),
1020
-// 			[]*api.ImageStream{
1021
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r1"},
1022
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"},
1023
-// 				{ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r3"},
1024
-// 			},
1025
-// 			[]bool{
1026
-// 				true,
1027
-// 				false,
1028
-// 				false,
1029
-// 			},
1030
-// 		},
1031
-// 	}
1032
-
1033
-// 	for _, tt := range tests {
1034
-// 		watching, err := registry.WatchImageStreams(kapi.NewDefaultContext(), tt.label, tt.field, "1")
1035
-// 		if err != nil {
1036
-// 			t.Fatalf("unexpected error: %v", err)
1037
-// 		}
1038
-// 		fakeClient.WaitForWatchCompletion()
1039
-
1040
-// 		for testIndex, stream := range tt.repos {
1041
-// 			// Set this value to avoid duplication in tests
1042
-// 			stream.Status.DockerImageRepository = stream.DockerImageRepository
1043
-// 			repoBytes, _ := latest.Codec.Encode(stream)
1044
-// 			fakeClient.WatchResponse <- &etcd.Response{
1045
-// 				Action: "set",
1046
-// 				Node: &etcd.Node{
1047
-// 					Value: string(repoBytes),
1048
-// 				},
1049
-// 			}
1050
-
1051
-// 			select {
1052
-// 			case event, ok := <-watching.ResultChan():
1053
-// 				if !ok {
1054
-// 					t.Errorf("watching channel should be open")
1055
-// 				}
1056
-// 				if !tt.expected[testIndex] {
1057
-// 					t.Errorf("unexpected imageStream returned from watch: %#v", event.Object)
1058
-// 				}
1059
-// 				if e, a := watch.Added, event.Type; e != a {
1060
-// 					t.Errorf("Expected %v, got %v", e, a)
1061
-// 				}
1062
-// 				if e, a := stream, event.Object; !reflect.DeepEqual(e, a) {
1063
-// 					t.Errorf("Expected %#v, got %#v", e, a)
1064
-// 				}
1065
-// 			case <-time.After(50 * time.Millisecond):
1066
-// 				if tt.expected[testIndex] {
1067
-// 					t.Errorf("Expected imageStream %#v to be returned from watch", stream)
1068
-// 				}
1069
-// 			}
1070
-// 		}
1071
-
1072
-// 		select {
1073
-// 		case _, ok := <-watching.ResultChan():
1074
-// 			if !ok {
1075
-// 				t.Errorf("watching channel should be open")
1076
-// 			}
1077
-// 		default:
1078
-// 		}
1079
-
1080
-// 		fakeClient.WatchInjectError <- nil
1081
-// 		if _, ok := <-watching.ResultChan(); ok {
1082
-// 			t.Errorf("watching channel should be closed")
1083
-// 		}
1084
-// 		watching.Stop()
1085
-// 	}
1086
-// }
1087
-
1088
-// func TestEtcdCreateImageStreamFailsWithoutNamespace(t *testing.T) {
1089
-// 	fakeClient := tools.NewFakeEtcdClient(t)
1090
-// 	fakeClient.TestIndex = true
1091
-// 	registry := NewTestEtcd(fakeClient)
1092
-// 	err := registry.CreateImageStream(kapi.NewContext(), &api.ImageStream{
1093
-// 		ObjectMeta: kapi.ObjectMeta{
1094
-// 			Name: "foo",
1095
-// 		},
1096
-// 	})
1097
-
1098
-// 	if err == nil {
1099
-// 		t.Errorf("expected error that namespace was missing from context")
1100
-// 	}
1101
-// }
1102
-
1103
-// func TestEtcdListImageStreamsInDifferentNamespaces(t *testing.T) {
1104
-// 	fakeClient := tools.NewFakeEtcdClient(t)
1105
-// 	namespaceAlfa := kapi.WithNamespace(kapi.NewContext(), "alfa")
1106
-// 	namespaceBravo := kapi.WithNamespace(kapi.NewContext(), "bravo")
1107
-// 	fakeClient.Data["/imagestreams/alfa"] = tools.EtcdResponseWithError{
1108
-// 		R: &etcd.Response{
1109
-// 			Node: &etcd.Node{
1110
-// 				Nodes: []*etcd.Node{
1111
-// 					{
1112
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo1"}}),
1113
-// 					},
1114
-// 				},
1115
-// 			},
1116
-// 		},
1117
-// 		E: nil,
1118
-// 	}
1119
-// 	fakeClient.Data["/imagestreams/bravo"] = tools.EtcdResponseWithError{
1120
-// 		R: &etcd.Response{
1121
-// 			Node: &etcd.Node{
1122
-// 				Nodes: []*etcd.Node{
1123
-// 					{
1124
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo2"}}),
1125
-// 					},
1126
-// 					{
1127
-// 						Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar2"}}),
1128
-// 					},
1129
-// 				},
1130
-// 			},
1131
-// 		},
1132
-// 		E: nil,
1133
-// 	}
1134
-// 	registry := NewTestEtcd(fakeClient)
1135
-
1136
-// 	imageStreamsAlfa, err := registry.ListImageStreams(namespaceAlfa, labels.Everything())
1137
-// 	if err != nil {
1138
-// 		t.Errorf("unexpected error: %v", err)
1139
-// 	}
1140
-// 	if len(imageStreamsAlfa.Items) != 1 || imageStreamsAlfa.Items[0].Name != "foo1" {
1141
-// 		t.Errorf("Unexpected imageStream list: %#v", imageStreamsAlfa)
1142
-// 	}
1143
-
1144
-// 	imageStreamsBravo, err := registry.ListImageStreams(namespaceBravo, labels.Everything())
1145
-// 	if err != nil {
1146
-// 		t.Errorf("unexpected error: %v", err)
1147
-// 	}
1148
-// 	if len(imageStreamsBravo.Items) != 2 || imageStreamsBravo.Items[0].Name != "foo2" || imageStreamsBravo.Items[1].Name != "bar2" {
1149
-// 		t.Errorf("Unexpected imageStream list: %#v", imageStreamsBravo)
1150
-// 	}
1151
-// }
1152
-
1153
-// func TestEtcdGetImageStreamInDifferentNamespaces(t *testing.T) {
1154
-// 	fakeClient := tools.NewFakeEtcdClient(t)
1155
-// 	namespaceAlfa := kapi.WithNamespace(kapi.NewContext(), "alfa")
1156
-// 	namespaceBravo := kapi.WithNamespace(kapi.NewContext(), "bravo")
1157
-// 	fakeClient.Set("/imagestreams/alfa/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0)
1158
-// 	fakeClient.Set("/imagestreams/bravo/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0)
1159
-// 	registry := NewTestEtcd(fakeClient)
1160
-
1161
-// 	alfaFoo, err := registry.GetImageStream(namespaceAlfa, "foo")
1162
-// 	if err != nil {
1163
-// 		t.Errorf("unexpected error: %v", err)
1164
-// 	}
1165
-// 	if alfaFoo == nil || alfaFoo.Name != "foo" {
1166
-// 		t.Errorf("Unexpected deployment: %#v", alfaFoo)
1167
-// 	}
1168
-
1169
-// 	bravoFoo, err := registry.GetImageStream(namespaceBravo, "foo")
1170
-// 	if err != nil {
1171
-// 		t.Errorf("unexpected error: %v", err)
1172
-// 	}
1173
-// 	if bravoFoo == nil || bravoFoo.Name != "foo" {
1174
-// 		t.Errorf("Unexpected deployment: %#v", bravoFoo)
1175
-// 	}
1176
-// }
1177
-// */
1178
-// type fakeStrategy struct {
1179
-// 	imagestream.Strategy
1180
-// }
1181
-
1182
-// func (fakeStrategy) PrepareForCreate(obj runtime.Object) {
1183
-// 	stream := obj.(*api.ImageStream)
1184
-// 	stream.Annotations = map[string]string{"test": "PrepareForCreate"}
1185
-// }
1186
-
1187
-// func (fakeStrategy) PrepareForUpdate(obj, old runtime.Object) {
1188
-// 	stream := obj.(*api.ImageStream)
1189
-// 	stream.Annotations["test"] = "PrepareForUpdate"
1190
-// }
1191
-
1192
-// func TestStrategyPrepareMethods(t *testing.T) {
1193
-// 	_, helper := newStorage(t)
1194
-// 	storage, _, _ := NewREST(helper, testDefaultRegistry, &fakeSubjectAccessReviewRegistry{})
1195
-// 	stream := validNewStream()
1196
-// 	strategy := fakeStrategy{imagestream.NewStrategy(testDefaultRegistry, &fakeSubjectAccessReviewRegistry{})}
1197
-
1198
-// 	storage.store.CreateStrategy = strategy
1199
-// 	storage.store.UpdateStrategy = strategy
1200
-
1201
-// 	ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
1202
-// 	obj, err := storage.Create(ctx, stream)
1203
-// 	if err != nil {
1204
-// 		t.Fatalf("Unexpected error: %v", err)
1205
-// 	}
1206
-
1207
-// 	updatedStream := obj.(*api.ImageStream)
1208
-// 	if updatedStream.Annotations["test"] != "PrepareForCreate" {
1209
-// 		t.Errorf("Expected PrepareForCreate annotation")
1210
-// 	}
1211
-
1212
-// 	obj, _, err = storage.Update(ctx, updatedStream)
1213
-// 	if err != nil {
1214
-// 		t.Errorf("Unexpected error: %v", err)
1215
-// 	}
1216
-
1217
-// 	updatedStream = obj.(*api.ImageStream)
1218
-// 	if updatedStream.Annotations["test"] != "PrepareForUpdate" {
1219
-// 		t.Errorf("Expected PrepareForUpdate annotation")
1220
-// 	}
1221
-// }
... ...
@@ -33,18 +33,19 @@ type Strategy struct {
33 33
 	defaultRegistry   api.DefaultRegistry
34 34
 	tagVerifier       *TagVerifier
35 35
 	limitVerifier     imageadmission.LimitVerifier
36
-	ImageStreamGetter ResourceGetter
36
+	imageStreamGetter ResourceGetter
37 37
 }
38 38
 
39 39
 // NewStrategy is the default logic that applies when creating and updating
40 40
 // ImageStream objects via the REST API.
41
-func NewStrategy(defaultRegistry api.DefaultRegistry, subjectAccessReviewClient subjectaccessreview.Registry, limitVerifier imageadmission.LimitVerifier) Strategy {
41
+func NewStrategy(defaultRegistry api.DefaultRegistry, subjectAccessReviewClient subjectaccessreview.Registry, limitVerifier imageadmission.LimitVerifier, imageStreamGetter ResourceGetter) Strategy {
42 42
 	return Strategy{
43
-		ObjectTyper:     kapi.Scheme,
44
-		NameGenerator:   kapi.SimpleNameGenerator,
45
-		defaultRegistry: defaultRegistry,
46
-		limitVerifier:   limitVerifier,
47
-		tagVerifier:     &TagVerifier{subjectAccessReviewClient},
43
+		ObjectTyper:       kapi.Scheme,
44
+		NameGenerator:     kapi.SimpleNameGenerator,
45
+		defaultRegistry:   defaultRegistry,
46
+		tagVerifier:       &TagVerifier{subjectAccessReviewClient},
47
+		limitVerifier:     limitVerifier,
48
+		imageStreamGetter: imageStreamGetter,
48 49
 	}
49 50
 }
50 51
 
... ...
@@ -53,9 +54,31 @@ func (s Strategy) NamespaceScoped() bool {
53 53
 	return true
54 54
 }
55 55
 
56
-// PrepareForCreate clears fields that are not allowed to be set by end users on creation,
56
+// BeforeCreate checks a number of creation preconditions before validate is called.
57 57
 // and verifies the current user is authorized to access any image streams newly referenced
58 58
 // in spec.tags.
59
+// TODO: this should be part of PrepareForCreate by allowing it to return errors.
60
+func (s Strategy) BeforeCreate(ctx kapi.Context, obj runtime.Object) error {
61
+	stream := obj.(*api.ImageStream)
62
+	user, ok := kapi.UserFrom(ctx)
63
+	if !ok {
64
+		return kerrors.NewForbidden(unversioned.GroupResource{Resource: "imagestreams"}, stream.Name, fmt.Errorf("no user context available"))
65
+	}
66
+
67
+	errs := s.tagVerifier.Verify(nil, stream, user)
68
+	errs = append(errs, s.tagsChanged(nil, stream)...)
69
+	if len(errs) > 0 {
70
+		return kerrors.NewInvalid(unversioned.GroupKind{Kind: "imagestreams"}, stream.Name, errs)
71
+	}
72
+
73
+	ns, ok := kapi.NamespaceFrom(ctx)
74
+	if !ok {
75
+		ns = stream.Namespace
76
+	}
77
+	return s.limitVerifier.VerifyLimits(ns, stream)
78
+}
79
+
80
+// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
59 81
 func (s Strategy) PrepareForCreate(ctx kapi.Context, obj runtime.Object) {
60 82
 	stream := obj.(*api.ImageStream)
61 83
 	stream.Status = api.ImageStreamStatus{
... ...
@@ -71,24 +94,7 @@ func (s Strategy) PrepareForCreate(ctx kapi.Context, obj runtime.Object) {
71 71
 
72 72
 // Validate validates a new image stream.
73 73
 func (s Strategy) Validate(ctx kapi.Context, obj runtime.Object) field.ErrorList {
74
-	stream := obj.(*api.ImageStream)
75
-	user, ok := kapi.UserFrom(ctx)
76
-	if !ok {
77
-		return field.ErrorList{field.Forbidden(field.NewPath("imageStream"), stream.Name)}
78
-	}
79
-	errs := s.tagVerifier.Verify(nil, stream, user)
80
-	errs = append(errs, s.tagsChanged(nil, stream)...)
81
-
82
-	ns, ok := kapi.NamespaceFrom(ctx)
83
-	if !ok {
84
-		ns = stream.Namespace
85
-	}
86
-	if err := s.limitVerifier.VerifyLimits(ns, stream); err != nil {
87
-		errs = append(errs, field.Forbidden(field.NewPath("imageStream"), err.Error()))
88
-	}
89
-
90
-	errs = append(errs, validation.ValidateImageStream(stream)...)
91
-	return errs
74
+	return validation.ValidateImageStream(obj.(*api.ImageStream))
92 75
 }
93 76
 
94 77
 // AllowCreateOnUpdate is false for image streams.
... ...
@@ -201,7 +207,7 @@ func (s Strategy) tagsChanged(old, stream *api.ImageStream) field.ErrorList {
201 201
 			streamRefNamespace = stream.Namespace
202 202
 		}
203 203
 		if streamRefNamespace != stream.Namespace || tagRefStreamName != stream.Name {
204
-			obj, err := s.ImageStreamGetter.Get(kapi.WithNamespace(kapi.NewContext(), streamRefNamespace), tagRefStreamName)
204
+			obj, err := s.imageStreamGetter.Get(kapi.WithNamespace(kapi.NewContext(), streamRefNamespace), tagRefStreamName)
205 205
 			if err != nil {
206 206
 				if kerrors.IsNotFound(err) {
207 207
 					errs = append(errs, field.NotFound(fromPath.Child("name"), tagRef.From.Name))
... ...
@@ -481,33 +487,39 @@ func (s Strategy) prepareForUpdate(obj, old runtime.Object, resetStatus bool) {
481 481
 	ensureSpecTagGenerationsAreSet(stream, oldStream)
482 482
 }
483 483
 
484
-func (s Strategy) PrepareForUpdate(ctx kapi.Context, obj, old runtime.Object) {
485
-	s.prepareForUpdate(obj, old, true)
486
-}
487
-
488
-// ValidateUpdate is the default update validation for an end user.
489
-func (s Strategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) field.ErrorList {
484
+// BeforeUpdate handles any transformations required before validation or update.
485
+// TODO: this should be part of PrepareForUpdate by allowing it to return errors.
486
+func (s Strategy) BeforeUpdate(ctx kapi.Context, obj, old runtime.Object) error {
490 487
 	stream := obj.(*api.ImageStream)
488
+	oldStream := old.(*api.ImageStream)
491 489
 
492 490
 	user, ok := kapi.UserFrom(ctx)
493 491
 	if !ok {
494
-		return field.ErrorList{field.Forbidden(field.NewPath("imageStream"), stream.Name)}
492
+		return kerrors.NewForbidden(unversioned.GroupResource{Resource: "imagestreams"}, stream.Name, fmt.Errorf("no user context available"))
495 493
 	}
496
-	oldStream := old.(*api.ImageStream)
497 494
 
498 495
 	errs := s.tagVerifier.Verify(oldStream, stream, user)
499 496
 	errs = append(errs, s.tagsChanged(oldStream, stream)...)
497
+	if len(errs) > 0 {
498
+		return kerrors.NewInvalid(unversioned.GroupKind{Kind: "imagestreams"}, stream.Name, errs)
499
+	}
500 500
 
501 501
 	ns, ok := kapi.NamespaceFrom(ctx)
502 502
 	if !ok {
503 503
 		ns = stream.Namespace
504 504
 	}
505
-	if err := s.limitVerifier.VerifyLimits(ns, stream); err != nil {
506
-		errs = append(errs, field.Forbidden(field.NewPath("imageStream"), err.Error()))
507
-	}
505
+	return s.limitVerifier.VerifyLimits(ns, stream)
506
+}
508 507
 
509
-	errs = append(errs, validation.ValidateImageStreamUpdate(stream, oldStream)...)
510
-	return errs
508
+func (s Strategy) PrepareForUpdate(ctx kapi.Context, obj, old runtime.Object) {
509
+	s.prepareForUpdate(obj, old, true)
510
+}
511
+
512
+// ValidateUpdate is the default update validation for an end user.
513
+func (s Strategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) field.ErrorList {
514
+	stream := obj.(*api.ImageStream)
515
+	oldStream := old.(*api.ImageStream)
516
+	return validation.ValidateImageStreamUpdate(stream, oldStream)
511 517
 }
512 518
 
513 519
 // Decorate decorates stream.Status.DockerImageRepository using the logic from
... ...
@@ -133,7 +133,7 @@ func TestDockerImageRepository(t *testing.T) {
133 133
 	}
134 134
 
135 135
 	for testName, test := range tests {
136
-		strategy := NewStrategy(&fakeDefaultRegistry{test.defaultRegistry}, &fakeSubjectAccessReviewRegistry{}, &testutil.FakeImageStreamLimitVerifier{})
136
+		strategy := NewStrategy(&fakeDefaultRegistry{test.defaultRegistry}, &fakeSubjectAccessReviewRegistry{}, &testutil.FakeImageStreamLimitVerifier{}, nil)
137 137
 		value := strategy.dockerImageRepository(test.stream)
138 138
 		if e, a := test.expected, value; e != a {
139 139
 			t.Errorf("%s: expected %q, got %q", testName, e, a)
... ...
@@ -366,7 +366,7 @@ func TestLimitVerifier(t *testing.T) {
366 366
 		name        string
367 367
 		isEvaluator func(string, *api.ImageStream) error
368 368
 		is          api.ImageStream
369
-		expected    field.ErrorList
369
+		expected    error
370 370
 	}{
371 371
 		{
372 372
 			name: "no limit",
... ...
@@ -442,9 +442,7 @@ func TestLimitVerifier(t *testing.T) {
442 442
 				},
443 443
 			},
444 444
 			isEvaluator: makeISEvaluator(1, 0),
445
-			expected: field.ErrorList{
446
-				field.Forbidden(field.NewPath("imageStream"), makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamImages}).Error()),
447
-			},
445
+			expected:    makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamImages}),
448 446
 		},
449 447
 
450 448
 		{
... ...
@@ -467,9 +465,7 @@ func TestLimitVerifier(t *testing.T) {
467 467
 				},
468 468
 			},
469 469
 			isEvaluator: makeISEvaluator(0, 0),
470
-			expected: field.ErrorList{
471
-				field.Forbidden(field.NewPath("imageStream"), makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamTags}).Error()),
472
-			},
470
+			expected:    makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamTags}),
473 471
 		},
474 472
 
475 473
 		{
... ...
@@ -504,9 +500,7 @@ func TestLimitVerifier(t *testing.T) {
504 504
 				},
505 505
 			},
506 506
 			isEvaluator: makeISEvaluator(0, 0),
507
-			expected: field.ErrorList{
508
-				field.Forbidden(field.NewPath("imageStream"), makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamImages, api.ResourceImageStreamTags}).Error()),
509
-			},
507
+			expected:    makeISForbiddenError("is", []kapi.ResourceName{api.ResourceImageStreamImages, api.ResourceImageStreamTags}),
510 508
 		},
511 509
 	}
512 510
 
... ...
@@ -525,10 +519,15 @@ func TestLimitVerifier(t *testing.T) {
525 525
 		}
526 526
 
527 527
 		ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{})
528
-		errList := s.Validate(ctx, &tc.is)
528
+		err := s.BeforeCreate(ctx, &tc.is)
529
+		if e, a := tc.expected, err; !reflect.DeepEqual(e, a) {
530
+			t.Errorf("%s: unexpected validation errors: %s", tc.name, diff.ObjectReflectDiff(e, a))
531
+		}
529 532
 
530
-		if e, a := tc.expected, errList; !reflect.DeepEqual(e, a) {
531
-			t.Errorf("%s: unexpected validation errors: %s", tc.name, diff.ObjectDiff(e, a))
533
+		// Update must fail the exact same way
534
+		err = s.BeforeUpdate(ctx, &tc.is, &api.ImageStream{})
535
+		if e, a := tc.expected, err; !reflect.DeepEqual(e, a) {
536
+			t.Errorf("%s: unexpected validation errors: %s", tc.name, diff.ObjectReflectDiff(e, a))
532 537
 		}
533 538
 	}
534 539
 }
... ...
@@ -1065,9 +1064,9 @@ func TestTagsChanged(t *testing.T) {
1065 1065
 		}
1066 1066
 
1067 1067
 		s := &Strategy{
1068
-			defaultRegistry: &fakeDefaultRegistry{},
1068
+			defaultRegistry:   &fakeDefaultRegistry{},
1069
+			imageStreamGetter: &fakeImageStreamGetter{test.otherStream},
1069 1070
 		}
1070
-		s.ImageStreamGetter = &fakeImageStreamGetter{test.otherStream}
1071 1071
 		err := s.tagsChanged(previousStream, stream)
1072 1072
 		if len(err) > 0 {
1073 1073
 			t.Errorf("%s: unable to process tags: %v", testName, err)
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"time"
8 8
 
9 9
 	kapi "k8s.io/kubernetes/pkg/api"
10
+	kapierrors "k8s.io/kubernetes/pkg/api/errors"
10 11
 	"k8s.io/kubernetes/pkg/api/resource"
11 12
 	kclient "k8s.io/kubernetes/pkg/client/unversioned"
12 13
 	"k8s.io/kubernetes/pkg/util/wait"
... ...
@@ -487,13 +488,33 @@ func setupImageStreamAdmissionTest(t *testing.T) (*kclient.Client, *client.Clien
487 487
 		t.Errorf("unexpected error: %v", err)
488 488
 	}
489 489
 
490
-	_, err = client.ImageStreams(testutil.Namespace()).Create(newImageStreamWithSpecTags("src", nil))
491
-	if err != nil {
492
-		t.Fatal(err)
490
+	for {
491
+		_, err = client.ImageStreams(testutil.Namespace()).Create(newImageStreamWithSpecTags("src", nil))
492
+		t.Logf("initing: %v", err)
493
+		if err != nil {
494
+			if errForbiddenWithRetry(err) {
495
+				t.Logf("waiting for limit ranger to catch up: %v", err)
496
+				continue
497
+			}
498
+			t.Fatalf("err: %#v", err)
499
+		}
500
+		break
493 501
 	}
494 502
 	return kClient, client
495 503
 }
496 504
 
505
+// errForbiddenWithRetry returns true if this is a status error and has requested a retry
506
+func errForbiddenWithRetry(err error) bool {
507
+	if err == nil || !kapierrors.IsForbidden(err) {
508
+		return false
509
+	}
510
+	status, ok := err.(kapierrors.APIStatus)
511
+	if !ok {
512
+		return false
513
+	}
514
+	return status.Status().Details != nil && status.Status().Details.RetryAfterSeconds > 0
515
+}
516
+
497 517
 // createResourceQuota creates a resource quota with given hard limits in a current namespace and waits until
498 518
 // a first usage refresh
499 519
 func createResourceQuota(t *testing.T, rqClient kclient.ResourceQuotaInterface, quotaName string, hard kapi.ResourceList) *kapi.ResourceQuota {