Use shared informer, simplify code in limiter.
| ... | ... |
@@ -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 {
|