diff --git a/pkg/image/api/v1/conversion_test.go b/pkg/image/api/v1/conversion_test.go index 696a20d078f7..c405d0c3e703 100644 --- a/pkg/image/api/v1/conversion_test.go +++ b/pkg/image/api/v1/conversion_test.go @@ -7,8 +7,12 @@ import ( kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/util" - _ "github.com/openshift/origin/pkg/api/latest" newer "github.com/openshift/origin/pkg/image/api" + _ "github.com/openshift/origin/pkg/image/api/docker10" + _ "github.com/openshift/origin/pkg/image/api/dockerpre012" + _ "github.com/openshift/origin/pkg/image/api/install" + _ "github.com/openshift/origin/pkg/image/api/v1" + _ "github.com/openshift/origin/pkg/image/api/v1beta3" testutil "github.com/openshift/origin/test/util/api" ) diff --git a/pkg/image/registry/image/etcd/etcd.go b/pkg/image/registry/image/etcd/etcd.go index 0e21ec39bbcb..84b7416c5c93 100644 --- a/pkg/image/registry/image/etcd/etcd.go +++ b/pkg/image/registry/image/etcd/etcd.go @@ -1,12 +1,12 @@ package etcd import ( - "errors" - kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/registry/generic" etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" @@ -18,65 +18,57 @@ import ( // REST implements a RESTStorage for images against etcd. type REST struct { - store *etcdgeneric.Etcd + *etcdgeneric.Etcd } // NewREST returns a new REST. func NewREST(s storage.Interface) *REST { prefix := "/images" + store := &etcdgeneric.Etcd{ - NewFunc: func() runtime.Object { return &api.Image{} }, + NewFunc: func() runtime.Object { return &api.Image{} }, + + // NewListFunc returns an object capable of storing results of an etcd list. NewListFunc: func() runtime.Object { return &api.ImageList{} }, + // Produces a path that etcd understands, to the root of the resource + // by combining the namespace in the context with the given prefix. + // Yet images are not namespace scoped, so we're returning just prefix here. KeyRootFunc: func(ctx kapi.Context) string { - // images are not namespace scoped return prefix }, + // Produces a path that etcd understands, to the resource by combining + // the namespace in the context with the given prefix + // Yet images are not namespace scoped, so we're returning just prefix here. KeyFunc: func(ctx kapi.Context, name string) (string, error) { - // images are not namespace scoped return etcdgeneric.NoNamespaceKeyFunc(ctx, prefix, name) }, + // Retrieve the name field of an image ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Image).Name, nil }, + // Used to match objects based on labels/fields for list and watch + PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { + return image.MatchImage(label, field) + }, EndpointName: "image", + // Used to validate image creation CreateStrategy: image.Strategy, + + // Used to validate image updates UpdateStrategy: image.Strategy, ReturnDeletedObject: false, Storage: s, } - return &REST{store: store} -} - -// New returns a new object -func (r *REST) New() runtime.Object { - return r.store.NewFunc() -} - -// NewList returns a new list object -func (r *REST) NewList() runtime.Object { - return r.store.NewListFunc() -} - -// List obtains a list of images with labels that match selector. -func (r *REST) List(ctx kapi.Context, options *unversioned.ListOptions) (runtime.Object, error) { - label := labels.Everything() - if options != nil && options.LabelSelector.Selector != nil { - label = options.LabelSelector.Selector - } - field := fields.Everything() - if options != nil && options.FieldSelector.Selector != nil { - field = options.FieldSelector.Selector - } - return r.store.ListPredicate(ctx, image.MatchImage(label, field), options) + return &REST{store} } // Watch begins watching for new, changed, or deleted images. func (r *REST) Watch(ctx kapi.Context, options *unversioned.ListOptions) (watch.Interface, error) { - if !options.FieldSelector.Selector.Empty() { - return nil, errors.New("field selectors are not supported on images") + if options != nil && options.FieldSelector.Selector != nil { + return nil, errors.NewBadRequest("field selectors are not supported on images") } label := labels.Everything() if options != nil && options.LabelSelector.Selector != nil { @@ -87,25 +79,5 @@ func (r *REST) Watch(ctx kapi.Context, options *unversioned.ListOptions) (watch. if options != nil { resourceVersion = options.ResourceVersion } - return r.store.WatchPredicate(ctx, image.MatchImage(label, field), resourceVersion) -} - -// Get gets a specific image specified by its ID. -func (r *REST) Get(ctx kapi.Context, name string) (runtime.Object, error) { - return r.store.Get(ctx, name) -} - -// Create creates an image based on a specification. -func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { - return r.store.Create(ctx, obj) -} - -// Update alters an existing image. -func (r *REST) Update(ctx kapi.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) -} - -// Delete deletes an existing image specified by its ID. -func (r *REST) Delete(ctx kapi.Context, name string, options *kapi.DeleteOptions) (runtime.Object, error) { - return r.store.Delete(ctx, name, options) + return r.WatchPredicate(ctx, image.MatchImage(label, field), resourceVersion) } diff --git a/pkg/image/registry/image/etcd/etcd_test.go b/pkg/image/registry/image/etcd/etcd_test.go index 719d22e2b6d6..241c723b4004 100644 --- a/pkg/image/registry/image/etcd/etcd_test.go +++ b/pkg/image/registry/image/etcd/etcd_test.go @@ -2,428 +2,112 @@ package etcd import ( "fmt" - "reflect" "testing" "time" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/rest" + klatest "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/storage" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" - "k8s.io/kubernetes/pkg/tools" - "k8s.io/kubernetes/pkg/tools/etcdtest" - "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" + etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing" "k8s.io/kubernetes/pkg/watch" - "github.com/coreos/go-etcd/etcd" - "github.com/openshift/origin/pkg/api/latest" "github.com/openshift/origin/pkg/image/api" + // Ensure the APIs are initilized. + _ "github.com/openshift/origin/pkg/image/api/docker10" + _ "github.com/openshift/origin/pkg/image/api/dockerpre012" + _ "github.com/openshift/origin/pkg/image/api/install" + _ "github.com/openshift/origin/pkg/image/api/v1" + _ "github.com/openshift/origin/pkg/image/api/v1beta3" "github.com/openshift/origin/pkg/image/registry/image" ) -// This copy and paste is not pure ignorance. This is that we can be sure that the key is getting made as we -// expect it to. If someone changes the location of these resources by say moving all the resources to -// "/origin/resources" (which is a really good idea), then they've made a breaking change and something should -// fail to let them know they've change some significant change and that other dependent pieces may break. -func makeTestImageListKey(namespace string) string { - if len(namespace) != 0 { - return "/images/" + namespace - } - return "/images" -} -func makeTestImageKey(namespace, id string) string { - return "/images/" + namespace + "/" + id -} -func makeTestDefaultImageKey(id string) string { - return makeTestImageKey(kapi.NamespaceDefault, id) -} -func makeTestDefaultImageListKey() string { - return makeTestImageListKey(kapi.NamespaceDefault) -} -func makeTestImageRepositoriesListKey(namespace string) string { - if len(namespace) != 0 { - return "/imageRepositories/" + namespace - } - return "/imageRepositories" -} -func makeTestImageRepositoriesKey(namespace, id string) string { - return "/imageRepositories/" + namespace + "/" + id -} -func makeTestDefaultImageRepositoriesKey(id string) string { - return makeTestImageRepositoriesKey(kapi.NamespaceDefault, id) -} -func makeTestDefaultImageRepositoriesListKey() string { - return makeTestImageRepositoriesListKey(kapi.NamespaceDefault) -} - -func newHelper(t *testing.T) (*tools.FakeEtcdClient, storage.Interface) { - fakeEtcdClient := tools.NewFakeEtcdClient(t) - fakeEtcdClient.TestIndex = true - helper := etcdstorage.NewEtcdStorage(fakeEtcdClient, latest.Codec, etcdtest.PathPrefix()) - return fakeEtcdClient, helper -} - -func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) { - etcdStorage, fakeClient := registrytest.NewEtcdStorage(t, "") - return NewREST(etcdStorage), fakeClient +func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) { + server := etcdtesting.NewEtcdTestClientServer(t) + etcdStorage := etcdstorage.NewEtcdStorage(server.Client, klatest.GroupOrDie("").Codec, etcdtest.PathPrefix()) + imageStorage := NewREST(etcdStorage) + return imageStorage, server } func TestStorage(t *testing.T) { - _, helper := newHelper(t) - storage := NewREST(helper) + storage, _ := newStorage(t) image.NewRegistry(storage) } -func validNewImage() *api.Image { +func validImage() *api.Image { return &api.Image{ ObjectMeta: kapi.ObjectMeta{ - Name: "foo", + Name: "foo", + GenerateName: "foo", }, DockerImageReference: "openshift/origin", } } func TestCreate(t *testing.T) { - storage, fakeClient := newStorage(t) - test := registrytest.New(t, fakeClient, storage.store).ClusterScope() - image := validNewImage() - image.ObjectMeta = kapi.ObjectMeta{GenerateName: "foo"} + storage, server := newStorage(t) + defer server.Terminate(t) + test := registrytest.New(t, storage.Etcd).ClusterScope() test.TestCreate( - // valid - image, + validImage(), // invalid &api.Image{}, ) } -func TestCreateRegistryError(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("test error") - storage := NewREST(helper) - - image := validNewImage() - _, err := storage.Create(kapi.NewDefaultContext(), image) - if err != fakeEtcdClient.Err { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestCreateAlreadyExists(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.TestIndex = true - - storage := NewREST(helper) - - existingImage := &api.Image{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - ResourceVersion: "1", - }, - DockerImageReference: "foo/bar:abcd1234", - } - - fakeEtcdClient.Data[etcdtest.AddPrefix("/images/foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, existingImage), - CreatedIndex: 1, - ModifiedIndex: 1, - }, - }, - } - _, err := storage.Create(kapi.NewDefaultContext(), &api.Image{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - }, - DockerImageReference: "foo/bar:abcd1234", - }) - if err == nil { - t.Fatalf("Unexpected non error") - } - if !errors.IsAlreadyExists(err) { - t.Errorf("Expected already exists error, got %s", err) - } -} - -func TestListError(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("test error") - storage := NewREST(helper) - images, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) - if err != fakeEtcdClient.Err { - t.Fatalf("Expected %#v, Got %#v", fakeEtcdClient.Err, err) - } - if images != nil { - t.Errorf("Unexpected non-nil image list: %#v", images) - } -} - -func TestListEmptyList(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.ChangeIndex = 1 - fakeEtcdClient.Data[etcdtest.AddPrefix("/images")] = tools.EtcdResponseWithError{ - R: &etcd.Response{}, - E: fakeEtcdClient.NewError(tools.EtcdErrorCodeNotFound), - } - storage := NewREST(helper) - images, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) - if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) - } - - if len(images.(*api.ImageList).Items) != 0 { - t.Errorf("Unexpected non-zero images list: %#v", images) - } - if images.(*api.ImageList).ResourceVersion != "1" { - t.Errorf("Unexpected resource version: %#v", images) - } -} - -func TestListPopulatedList(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.ChangeIndex = 1 - fakeEtcdClient.Data[etcdtest.AddPrefix("/images")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - {Value: runtime.EncodeOrDie(latest.Codec, &api.Image{ObjectMeta: kapi.ObjectMeta{Name: "foo"}})}, - {Value: runtime.EncodeOrDie(latest.Codec, &api.Image{ObjectMeta: kapi.ObjectMeta{Name: "bar"}})}, - }, - }, - }, - } - - storage := NewREST(helper) - - list, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) - if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) - } - - images := list.(*api.ImageList) - - if e, a := 2, len(images.Items); e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} - -func TestListFiltered(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.ChangeIndex = 1 - fakeEtcdClient.Data[etcdtest.AddPrefix("/images")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.Image{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"env": "prod"}, - }, - }), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &api.Image{ - ObjectMeta: kapi.ObjectMeta{ - Name: "bar", - Labels: map[string]string{"env": "dev"}, - }, - }), - }, - }, - }, - }, - E: nil, - } - storage := NewREST(helper) - list, err := storage.List(kapi.NewDefaultContext(), labels.SelectorFromSet(labels.Set{"env": "dev"}), fields.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - images := list.(*api.ImageList) - if len(images.Items) != 1 || images.Items[0].Name != "bar" { - t.Errorf("Unexpected images list: %#v", images) - } -} - -func TestCreateMissingID(t *testing.T) { - _, helper := newHelper(t) - storage := NewREST(helper) - - obj, err := storage.Create(kapi.NewDefaultContext(), &api.Image{}) - if obj != nil { - t.Errorf("Expected nil obj, got %v", obj) - } - if !errors.IsInvalid(err) { - t.Errorf("Expected 'invalid' error, got %v", err) - } -} - -func TestCreateOK(t *testing.T) { - _, helper := newHelper(t) - storage := NewREST(helper) - - obj, err := storage.Create(kapi.NewDefaultContext(), &api.Image{ - ObjectMeta: kapi.ObjectMeta{Name: "foo"}, - DockerImageReference: "openshift/ruby-19-centos", - }) - if obj == nil { - t.Errorf("Expected nil obj, got %v", obj) - } - if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) - } - - image, ok := obj.(*api.Image) - if !ok { - t.Errorf("Expected image type, got: %#v", obj) - } - if image.Name != "foo" { - t.Errorf("Unexpected image: %#v", image) - } -} - -func TestGetError(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("bad") - storage := NewREST(helper) - - image, err := storage.Get(kapi.NewDefaultContext(), "foo") - if image != nil { - t.Errorf("Unexpected non-nil image: %#v", image) - } - if err != fakeEtcdClient.Err { - t.Errorf("Expected %v, got %v", fakeEtcdClient.Err, err) - } -} - -func TestGetNotFound(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - storage := NewREST(helper) - fakeEtcdClient.Data[etcdtest.AddPrefix("/images/foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: tools.EtcdErrorNotFound, - } - - image, err := storage.Get(kapi.NewDefaultContext(), "foo") - if err == nil { - t.Errorf("Unexpected non-error.") - } - if image != nil { - t.Errorf("Unexpected image: %#v", image) - } +func TestList(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + test := registrytest.New(t, storage.Etcd).ClusterScope() + test.TestList( + validImage(), + ) } -func TestGetOK(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - expectedImage := &api.Image{ - ObjectMeta: kapi.ObjectMeta{Name: "foo"}, - DockerImageReference: "openshift/ruby-19-centos", - } - fakeEtcdClient.Data[etcdtest.AddPrefix("/images/foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, expectedImage), - }, - }, - } - storage := NewREST(helper) - - image, err := storage.Get(kapi.NewDefaultContext(), "foo") - if image == nil { - t.Fatal("Unexpected nil image") - } - if err != nil { - t.Fatal("Unexpected non-nil error", err) - } - if image.(*api.Image).Name != "foo" { - t.Errorf("Unexpected image: %#v", image) - } +func TestGet(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + test := registrytest.New(t, storage.Etcd).ClusterScope() + test.TestGet( + validImage(), + ) } func TestDelete(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Data[etcdtest.AddPrefix("/images/foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.Image{}), - }, - }, - } - storage := NewREST(helper) - - obj, err := storage.Delete(kapi.NewDefaultContext(), "foo", nil) - - if obj == nil { - t.Error("Unexpected nil obj") - } - if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) - } - - status, ok := obj.(*unversioned.Status) - if !ok { - t.Fatalf("Expected status type, got: %#v", obj) - } - if status.Status != unversioned.StatusSuccess { - t.Errorf("Expected status=success, got: %#v", status) - } - if len(fakeEtcdClient.DeletedKeys) != 1 { - t.Errorf("Expected 1 delete, found %#v", fakeEtcdClient.DeletedKeys) - } else if key := etcdtest.AddPrefix("/images/foo"); fakeEtcdClient.DeletedKeys[0] != key { - t.Errorf("Unexpected key: %s, expected %s", fakeEtcdClient.DeletedKeys[0], key) - } -} - -func TestDeleteNotFound(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = tools.EtcdErrorNotFound - storage := NewREST(helper) - _, err := storage.Delete(kapi.NewDefaultContext(), "foo", nil) - if err == nil { - t.Error("Unexpected non-error") - } - if !errors.IsNotFound(err) { - t.Errorf("Expected 'not found' error, got %#v", err) - } + storage, server := newStorage(t) + defer server.Terminate(t) + test := registrytest.New(t, storage.Etcd).ClusterScope() + image := validImage() + image.ObjectMeta = kapi.ObjectMeta{GenerateName: "foo"} + test.TestDelete( + validImage(), + ) } -func TestDeleteImageError(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("Some error") - storage := NewREST(helper) - _, err := storage.Delete(kapi.NewDefaultContext(), "foo", nil) - if err == nil { - t.Error("Unexpected non-error") +func TestWatchErrorFieldSet(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + options := &unversioned.ListOptions{ + FieldSelector: unversioned.FieldSelector{fields.SelectorFromSet(fields.Set{"foo": "bar"})}, } -} - -func TestWatchErrorWithFieldSet(t *testing.T) { - _, helper := newHelper(t) - storage := NewREST(helper) - - _, err := storage.Watch(kapi.NewDefaultContext(), labels.Everything(), fields.SelectorFromSet(fields.Set{"foo": "bar"}), "1") + _, err := storage.Watch(kapi.NewDefaultContext(), options) if err == nil { t.Fatal("unexpected nil error") } - if err.Error() != "field selectors are not supported on images" { + if !errors.IsBadRequest(err) || err.Error() != "field selectors are not supported on images" { t.Fatalf("unexpected error: %s", err.Error()) } } -func TestWatchOK(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - storage := NewREST(helper) - +func TestWatch(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) var tests = []struct { label labels.Selector images []*api.Image @@ -443,7 +127,7 @@ func TestWatchOK(t *testing.T) { }, }, { - labels.SelectorFromSet(labels.Set{"color": "blue"}), + labels.Set{"color": "blue"}.AsSelector(), []*api.Image{ {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: map[string]string{"color": "blue"}}, DockerImageMetadata: api.DockerImage{}}, {ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: map[string]string{"color": "green"}}, DockerImageMetadata: api.DockerImage{}}, @@ -456,20 +140,21 @@ func TestWatchOK(t *testing.T) { }, }, } - for _, tt := range tests { - watching, err := storage.Watch(kapi.NewDefaultContext(), tt.label, fields.Everything(), "1") + for idx, tt := range tests { + ctx := kapi.NewDefaultContext() + options := &unversioned.ListOptions{ + LabelSelector: unversioned.LabelSelector{tt.label}, + ResourceVersion: "1", + } + watching, err := storage.Watch(ctx, options) if err != nil { t.Fatalf("unexpected error: %v", err) } - fakeEtcdClient.WaitForWatchCompletion() for testIndex, image := range tt.images { - imageBytes, _ := latest.Codec.Encode(image) - fakeEtcdClient.WatchResponse <- &etcd.Response{ - Action: "set", - Node: &etcd.Node{ - Value: string(imageBytes), - }, + image.Name = fmt.Sprintf("%s%d", image.Name, idx) + if err := emitObject(storage, ctx, image); err != nil { + t.Errorf("unexpected error: %v", err) } select { @@ -483,58 +168,53 @@ func TestWatchOK(t *testing.T) { if e, a := watch.Added, event.Type; e != a { t.Errorf("Expected %v, got %v", e, a) } - image.DockerImageMetadataVersion = "1.0" - if e, a := image, event.Object; !reflect.DeepEqual(e, a) { - t.Errorf("Objects did not match: %s", util.ObjectDiff(e, a)) - } case <-time.After(50 * time.Millisecond): if tt.expected[testIndex] { - t.Errorf("Expected image %#v to be returned from watch", image) + t.Errorf("%d:%d: Expected image %#v to be returned from watch", idx, testIndex, image) } } } - - select { - case _, ok := <-watching.ResultChan(): - if !ok { - t.Errorf("watching channel should be open") - } - default: - } - - fakeEtcdClient.WatchInjectError <- nil - if _, ok := <-watching.ResultChan(); ok { - t.Errorf("watching channel should be closed") - } watching.Stop() } } -type fakeStrategy struct { - rest.RESTCreateStrategy +func emitObject(storage *REST, ctx kapi.Context, obj runtime.Object) error { + meta, err := kapi.ObjectMetaFor(obj) + if err != nil { + return err + } + key, err := storage.KeyFunc(ctx, meta.Name) + if err != nil { + return err + } + return storage.Storage.Set(ctx, key, obj, nil, 0) } -func (fakeStrategy) PrepareForCreate(obj runtime.Object) { - img := obj.(*api.Image) - img.Annotations = make(map[string]string, 1) - img.Annotations["test"] = "PrepareForCreate" -} +// type fakeStrategy struct { +// rest.RESTCreateStrategy +// } -func TestStrategyPrepareMethods(t *testing.T) { - _, helper := newHelper(t) - storage := NewREST(helper) - img := validNewImage() - strategy := fakeStrategy{image.Strategy} +// func (fakeStrategy) PrepareForCreate(obj runtime.Object) { +// img := obj.(*api.Image) +// img.Annotations = make(map[string]string, 1) +// img.Annotations["test"] = "PrepareForCreate" +// } - storage.store.CreateStrategy = strategy +// func TestStrategyPrepareMethods(t *testing.T) { +// _, helper := newHelper(t) +// storage := NewREST(helper) +// img := validImage() +// strategy := fakeStrategy{image.Strategy} - obj, err := storage.Create(kapi.NewDefaultContext(), img) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } +// storage.store.CreateStrategy = strategy - newImage := obj.(*api.Image) - if newImage.Annotations["test"] != "PrepareForCreate" { - t.Errorf("Expected PrepareForCreate annotation") - } -} +// obj, err := storage.Create(kapi.NewDefaultContext(), img) +// if err != nil { +// t.Errorf("Unexpected error: %v", err) +// } + +// newImage := obj.(*api.Image) +// if newImage.Annotations["test"] != "PrepareForCreate" { +// t.Errorf("Expected PrepareForCreate annotation") +// } +// } diff --git a/pkg/image/registry/imagestream/etcd/etcd.go b/pkg/image/registry/imagestream/etcd/etcd.go index 85d17e378808..8bda7abfcbb2 100644 --- a/pkg/image/registry/imagestream/etcd/etcd.go +++ b/pkg/image/registry/imagestream/etcd/etcd.go @@ -1,38 +1,52 @@ package etcd import ( - "github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview" - "github.com/openshift/origin/pkg/image/api" - "github.com/openshift/origin/pkg/image/registry/imagestream" kapi "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/registry/generic" etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" - "k8s.io/kubernetes/pkg/watch" + + "github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview" + "github.com/openshift/origin/pkg/image/api" + "github.com/openshift/origin/pkg/image/registry/imagestream" ) // REST implements a RESTStorage for image streams against etcd. type REST struct { - store *etcdgeneric.Etcd + *etcdgeneric.Etcd subjectAccessReviewRegistry subjectaccessreview.Registry } // NewREST returns a new REST. func NewREST(s storage.Interface, defaultRegistry imagestream.DefaultRegistry, subjectAccessReviewRegistry subjectaccessreview.Registry) (*REST, *StatusREST, *InternalREST) { prefix := "/imagestreams" + store := etcdgeneric.Etcd{ - NewFunc: func() runtime.Object { return &api.ImageStream{} }, + NewFunc: func() runtime.Object { return &api.ImageStream{} }, + + // NewListFunc returns an object capable of storing results of an etcd list. NewListFunc: func() runtime.Object { return &api.ImageStreamList{} }, + // Produces a path that etcd understands, to the root of the resource + // by combining the namespace in the context with the given prefix. KeyRootFunc: func(ctx kapi.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, + // Produces a path that etcd understands, to the resource by combining + // the namespace in the context with the given prefix KeyFunc: func(ctx kapi.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, + // Retrieve the name field of an image ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.ImageStream).Name, nil }, + // Used to match objects based on labels/fields for list and watch + PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { + return imagestream.MatchImageStream(label, field) + }, EndpointName: "imageStream", ReturnDeletedObject: false, @@ -53,71 +67,11 @@ func NewREST(s storage.Interface, defaultRegistry imagestream.DefaultRegistry, s store.UpdateStrategy = strategy store.Decorator = strategy.Decorate - rest.store = &store + rest.Etcd = &store return rest, &StatusREST{store: &statusStore}, &InternalREST{store: &internalStore} } -// New returns a new object -func (r *REST) New() runtime.Object { - return r.store.NewFunc() -} - -// NewList returns a new list object -func (r *REST) NewList() runtime.Object { - return r.store.NewListFunc() -} - -// List obtains a list of image streams with labels that match selector. -func (r *REST) List(ctx kapi.Context, options *unversioned.ListOptions) (runtime.Object, error) { - label := labels.Everything() - if options != nil && options.LabelSelector.Selector != nil { - label = options.LabelSelector.Selector - } - field := fields.Everything() - if options != nil && options.FieldSelector.Selector != nil { - field = options.FieldSelector.Selector - } - return r.store.ListPredicate(ctx, imagestream.MatchImageStream(label, field), options) -} - -// Watch begins watching for new, changed, or deleted image streams. -func (r *REST) Watch(ctx kapi.Context, options *unversioned.ListOptions) (watch.Interface, error) { - label := labels.Everything() - if options != nil && options.LabelSelector.Selector != nil { - label = options.LabelSelector.Selector - } - field := fields.Everything() - if options != nil && options.FieldSelector.Selector != nil { - field = options.FieldSelector.Selector - } - resourceVersion := "" - if options != nil { - resourceVersion = options.ResourceVersion - } - return r.store.WatchPredicate(ctx, imagestream.MatchImageStream(label, field), resourceVersion) -} - -// Get gets a specific image stream specified by its ID. -func (r *REST) Get(ctx kapi.Context, name string) (runtime.Object, error) { - return r.store.Get(ctx, name) -} - -// Create creates a image stream based on a specification. -func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { - return r.store.Create(ctx, obj) -} - -// Update changes a image stream specification. -func (r *REST) Update(ctx kapi.Context, obj runtime.Object) (runtime.Object, bool, error) { - return r.store.Update(ctx, obj) -} - -// Delete deletes an existing image stream specified by its ID. -func (r *REST) Delete(ctx kapi.Context, name string, options *kapi.DeleteOptions) (runtime.Object, error) { - return r.store.Delete(ctx, name, options) -} - // StatusREST implements the REST endpoint for changing the status of an image stream. type StatusREST struct { store *etcdgeneric.Etcd diff --git a/pkg/image/registry/imagestream/etcd/etcd_test.go b/pkg/image/registry/imagestream/etcd/etcd_test.go index 832c285eefc9..9e191a4e486d 100644 --- a/pkg/image/registry/imagestream/etcd/etcd_test.go +++ b/pkg/image/registry/imagestream/etcd/etcd_test.go @@ -1,30 +1,40 @@ package etcd import ( - "fmt" - "strings" + // "fmt" + // "strings" "testing" - "github.com/coreos/go-etcd/etcd" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/unversioned" + klatest "k8s.io/kubernetes/pkg/api/latest" + // "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/auth/user" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/storage" + // "k8s.io/kubernetes/pkg/fields" + // "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/registry/registrytest" + // "k8s.io/kubernetes/pkg/runtime" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" - "k8s.io/kubernetes/pkg/tools" - "k8s.io/kubernetes/pkg/tools/etcdtest" + "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" + etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing" + // storagetesting "k8s.io/kubernetes/pkg/storage/testing" - "github.com/openshift/origin/pkg/api/latest" authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview" "github.com/openshift/origin/pkg/image/api" + // Ensure the APIs are initilized. + _ "github.com/openshift/origin/pkg/image/api/docker10" + _ "github.com/openshift/origin/pkg/image/api/dockerpre012" + _ "github.com/openshift/origin/pkg/image/api/install" + _ "github.com/openshift/origin/pkg/image/api/v1" + _ "github.com/openshift/origin/pkg/image/api/v1beta3" "github.com/openshift/origin/pkg/image/registry/imagestream" ) +const ( + name = "foo" +) + var ( testDefaultRegistry = imagestream.DefaultRegistryFunc(func() (string, bool) { return "test", true }) noDefaultRegistry = imagestream.DefaultRegistryFunc(func() (string, bool) { return "", false }) @@ -45,130 +55,141 @@ func (f *fakeSubjectAccessReviewRegistry) CreateSubjectAccessReview(ctx kapi.Con return &authorizationapi.SubjectAccessReviewResponse{Allowed: f.allow}, f.err } -func newHelper(t *testing.T) (*tools.FakeEtcdClient, storage.Interface) { - fakeEtcdClient := tools.NewFakeEtcdClient(t) - fakeEtcdClient.TestIndex = true - helper := etcdstorage.NewEtcdStorage(fakeEtcdClient, latest.Codec, etcdtest.PathPrefix()) - return fakeEtcdClient, helper +func newStorage(t *testing.T) (*REST, *StatusREST, *InternalREST, *etcdtesting.EtcdTestServer) { + server := etcdtesting.NewEtcdTestClientServer(t) + etcdStorage := etcdstorage.NewEtcdStorage(server.Client, klatest.GroupOrDie("").Codec, etcdtest.PathPrefix()) + // etcdStorage, server := registrytest.NewEtcdStorage(t, "") + imageStorage, statusStorage, internalStorage := NewREST(etcdStorage, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + return imageStorage, statusStorage, internalStorage, server } -func validNewStream() *api.ImageStream { +func validImageStream() *api.ImageStream { return &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ - Name: "foo", + Name: name, }, } } -func TestCreate(t *testing.T) { - _, helper := newHelper(t) - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - stream := validNewStream() +func create(t *testing.T, storage *REST, obj *api.ImageStream) *api.ImageStream { ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - _, err := storage.Create(ctx, stream) + newObj, err := storage.Create(ctx, obj) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("Unexpected error: %v", err) } + return newObj.(*api.ImageStream) +} + +func TestCreate(t *testing.T) { + storage, _, _, server := newStorage(t) + defer server.Terminate(t) + + create(t, storage, validImageStream()) +} + +func TestList(t *testing.T) { + storage, _, _, server := newStorage(t) + defer server.Terminate(t) + test := registrytest.New(t, storage.Etcd) + test.TestList( + validImageStream(), + ) } func TestGetImageStreamError(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("foo") - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + storage, _, _, server := newStorage(t) + defer server.Terminate(t) image, err := storage.Get(kapi.NewDefaultContext(), "image1") + if !errors.IsNotFound(err) { + t.Errorf("Expected not-found error, got %v", err) + } if image != nil { t.Errorf("Unexpected non-nil image stream: %#v", image) } - if err != fakeEtcdClient.Err { - t.Errorf("Expected %#v, got %#v", fakeEtcdClient.Err, err) - } } func TestGetImageStreamOK(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + storage, _, _, server := newStorage(t) + defer server.Terminate(t) - ctx := kapi.NewDefaultContext() - repoName := "foo" - key, _ := storage.store.KeyFunc(ctx, repoName) - fakeEtcdClient.Set(etcdtest.AddPrefix(""+key), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: repoName}}), 0) + image := create(t, storage, validImageStream()) - obj, err := storage.Get(kapi.NewDefaultContext(), repoName) + obj, err := storage.Get(kapi.NewDefaultContext(), name) if err != nil { - t.Errorf("Unexpected non-nil error: %#v", err) + t.Errorf("Unexpected error: %#v", err) } if obj == nil { t.Fatalf("Unexpected nil stream") } - stream := obj.(*api.ImageStream) - if e, a := repoName, stream.Name; e != a { - t.Errorf("Expected %#v, got %#v", e, a) - } -} - -func TestListImageStreamsError(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("foo") - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - imageStreams, err := storage.List(kapi.NewDefaultContext(), nil, nil) - if err != fakeEtcdClient.Err { - t.Errorf("Expected %#v, Got %#v", fakeEtcdClient.Err, err) - } - - if imageStreams != nil { - t.Errorf("Unexpected non-nil imageStreams list: %#v", imageStreams) - } -} - -func TestListImageStreamsEmptyList(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.ChangeIndex = 1 - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default")] = tools.EtcdResponseWithError{ - R: &etcd.Response{}, - E: fakeEtcdClient.NewError(tools.EtcdErrorCodeNotFound), - } - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - imageStreams, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) - if err != nil { - t.Fatalf("Unexpected non-nil error: %#v", err) - } - if len(imageStreams.(*api.ImageStreamList).Items) != 0 { - t.Errorf("Unexpected non-zero imageStreams list: %#v", imageStreams) - } - if imageStreams.(*api.ImageStreamList).ResourceVersion != "1" { - t.Errorf("Unexpected resource version: %#v", imageStreams) + got := obj.(*api.ImageStream) + got.ResourceVersion = image.ResourceVersion + if !kapi.Semantic.DeepEqual(image, got) { + t.Errorf("Expected %#v, got %#v", image, got) } } -func TestListImageStreamsPopulatedList(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - {Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}})}, - {Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}})}, - }, - }, - }, - } - - list, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) - if err != nil { - t.Fatalf("Unexpected non-nil error: %#v", err) - } - - imageStreams := list.(*api.ImageStreamList) - - if e, a := 2, len(imageStreams.Items); e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} +// func TestListImageStreamsError(t *testing.T) { +// storage, _, _, server := newStorage(t) +// defer server.Terminate(t) + +// objs, err := storage.List(kapi.NewDefaultContext(), nil) +// if err != nil { +// t.Errorf("Expected err, got nothing") +// } +// got := objs.(*api.ImageStreamList) +// if got == nil || len(got.Items) != 0 { +// t.Errorf("Unexpected empty imageStreams list, got %#v", got) +// } +// } + +// func TestListImageStreamsEmptyList(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.ChangeIndex = 1 +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{}, +// E: fakeEtcdClient.NewError(tools.EtcdErrorCodeNotFound), +// } +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// imageStreams, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) +// if err != nil { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } +// if len(imageStreams.(*api.ImageStreamList).Items) != 0 { +// t.Errorf("Unexpected non-zero imageStreams list: %#v", imageStreams) +// } +// if imageStreams.(*api.ImageStreamList).ResourceVersion != "1" { +// t.Errorf("Unexpected resource version: %#v", imageStreams) +// } +// } + +// func TestListImageStreamsPopulatedList(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Nodes: []*etcd.Node{ +// {Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}})}, +// {Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}})}, +// }, +// }, +// }, +// } + +// list, err := storage.List(kapi.NewDefaultContext(), labels.Everything(), fields.Everything()) +// if err != nil { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } + +// imageStreams := list.(*api.ImageStreamList) + +// if e, a := 2, len(imageStreams.Items); e != a { +// t.Errorf("Expected %v, got %v", e, a) +// } +// } type fakeUser struct { } @@ -187,1020 +208,1020 @@ func (u *fakeUser) GetGroups() []string { return []string{"group1"} } -func TestCreateImageStreamOK(t *testing.T) { - _, helper := newHelper(t) - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - stream := &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}} - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - _, err := storage.Create(ctx, stream) - if err != nil { - t.Fatalf("Unexpected non-nil error: %#v", err) - } - - actual := &api.ImageStream{} - if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil { - t.Fatalf("unexpected extraction error: %v", err) - } - if actual.Name != stream.Name { - t.Errorf("unexpected stream: %#v", actual) - } - if len(actual.UID) == 0 { - t.Errorf("expected stream UID to be set: %#v", actual) - } - if stream.CreationTimestamp.IsZero() { - t.Error("Unexpected zero CreationTimestamp") - } - if stream.Spec.DockerImageRepository != "" { - t.Errorf("unexpected stream: %#v", stream) - } -} - -func TestCreateImageStreamSpecTagsFromSet(t *testing.T) { - tests := map[string]struct { - otherNamespace string - sarExpected bool - sarAllowed bool - }{ - "same namespace (blank), no sar": { - otherNamespace: "", - sarExpected: false, - }, - "same namespace (set), no sar": { - otherNamespace: "default", - sarExpected: false, - }, - "different namespace, sar allowed": { - otherNamespace: "otherns", - sarExpected: true, - sarAllowed: true, - }, - "different namespace, sar denied": { - otherNamespace: "otherns", - sarExpected: true, - sarAllowed: false, - }, - } - for name, test := range tests { - fakeEtcdClient, helper := newHelper(t) - sarRegistry := &fakeSubjectAccessReviewRegistry{ - allow: test.sarAllowed, - } - storage, _, _ := NewREST(helper, noDefaultRegistry, sarRegistry) - - otherNamespace := test.otherNamespace - if len(otherNamespace) == 0 { - otherNamespace = "default" - } - fakeEtcdClient.Data[fmt.Sprintf(etcdtest.AddPrefix("/imagestreams/%s/other"), otherNamespace)] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "other", Namespace: otherNamespace}, - Status: api.ImageStreamStatus{ - Tags: map[string]api.TagEventList{ - "latest": { - Items: []api.TagEvent{ - { - DockerImageReference: fmt.Sprintf("%s/other:latest", otherNamespace), - }, - }, - }, - }, - }, - }), - ModifiedIndex: 1, - }, - }, - } - - stream := &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "foo"}, - Spec: api.ImageStreamSpec{ - Tags: map[string]api.TagReference{ - "other": { - From: &kapi.ObjectReference{ - Kind: "ImageStreamTag", - Namespace: test.otherNamespace, - Name: "other:latest", - }, - }, - }, - }, - } - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - _, err := storage.Create(ctx, stream) - if test.sarExpected { - if sarRegistry.request == nil { - t.Errorf("%s: expected sar request", name) - continue - } - if e, a := test.sarAllowed, err == nil; e != a { - t.Errorf("%s: expected sarAllowed=%t, got error %t: %v", name, e, a, err) - continue - } - - continue - } - - // sar not expected - if err != nil { - t.Fatalf("%s: unexpected error: %v", name, err) - } - - actual := &api.ImageStream{} - if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil { - t.Fatalf("%s: unexpected extraction error: %v", name, err) - } - if e, a := fmt.Sprintf("%s/other:latest", otherNamespace), actual.Status.Tags["other"].Items[0].DockerImageReference; e != a { - t.Errorf("%s: dockerImageReference: expected %q, got %q", name, e, a) - } - } -} - -func TestCreateRegistryErrorSaving(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("foo") - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - _, err := storage.Create(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}) - if err != fakeEtcdClient.Err { - t.Fatalf("Unexpected non-nil error: %#v", err) - } -} - -func TestUpdateImageStreamMissingID(t *testing.T) { - _, helper := newHelper(t) - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - obj, created, err := storage.Update(kapi.NewDefaultContext(), &api.ImageStream{}) - if obj != nil || created { - t.Fatalf("Expected nil, got %v", obj) - } - if strings.Index(err.Error(), "Name parameter required") == -1 { - t.Errorf("Expected 'Name parameter required' error, got %v", err) - } -} - -func TestUpdateRegistryErrorSaving(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Err = fmt.Errorf("foo") - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - _, created, err := storage.Update(kapi.NewDefaultContext(), &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}}) - if err != fakeEtcdClient.Err || created { - t.Fatalf("Unexpected non-nil error: %#v", err) - } -} - -func TestUpdateImageStreamOK(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/bar")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "default"}, - }), - ModifiedIndex: 2, - }, - }, - } - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - obj, created, err := storage.Update(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar", ResourceVersion: "1"}}) - if !errors.IsConflict(err) { - t.Fatalf("unexpected non-error: %v", err) - } - obj, created, err = storage.Update(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar", ResourceVersion: "2"}}) - if err != nil || created { - t.Fatalf("Unexpected non-nil error: %#v", err) - } - stream, ok := obj.(*api.ImageStream) - if !ok { - t.Errorf("Expected image stream, got %#v", obj) - } - if stream.Name != "bar" { - t.Errorf("Unexpected stream returned: %#v", stream) - } -} - -func TestUpdateImageStreamSpecTagsFromSet(t *testing.T) { - tests := map[string]struct { - otherNamespace string - sarExpected bool - sarAllowed bool - }{ - "same namespace (blank), no sar": { - otherNamespace: "", - sarExpected: false, - }, - "same namespace (set), no sar": { - otherNamespace: "default", - sarExpected: false, - }, - "different namespace, sar allowed": { - otherNamespace: "otherns", - sarExpected: true, - sarAllowed: true, - }, - "different namespace, sar denied": { - otherNamespace: "otherns", - sarExpected: true, - sarAllowed: false, - }, - } - for name, test := range tests { - fakeEtcdClient, helper := newHelper(t) - sarRegistry := &fakeSubjectAccessReviewRegistry{ - allow: test.sarAllowed, - } - storage, _, _ := NewREST(helper, noDefaultRegistry, sarRegistry) - - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "default"}, - }), - ModifiedIndex: 1, - }, - }, - } - - otherNamespace := test.otherNamespace - if len(otherNamespace) == 0 { - otherNamespace = "default" - } - fakeEtcdClient.Data[fmt.Sprintf(etcdtest.AddPrefix("/imagestreams/%s/other"), otherNamespace)] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "other", Namespace: otherNamespace}, - Status: api.ImageStreamStatus{ - Tags: map[string]api.TagEventList{ - "latest": { - Items: []api.TagEvent{ - { - DockerImageReference: fmt.Sprintf("%s/other:latest", otherNamespace), - }, - }, - }, - }, - }, - }), - ModifiedIndex: 1, - }, - }, - } - - stream := &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - Spec: api.ImageStreamSpec{ - Tags: map[string]api.TagReference{ - "other": { - From: &kapi.ObjectReference{ - Kind: "ImageStreamTag", - Namespace: test.otherNamespace, - Name: "other:latest", - }, - }, - }, - }, - } - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - _, _, err := storage.Update(ctx, stream) - if test.sarExpected { - if sarRegistry.request == nil { - t.Errorf("%s: expected sar request", name) - continue - } - if e, a := test.sarAllowed, err == nil; e != a { - t.Errorf("%s: expected sarAllowed=%t, got error %t: %v", name, e, a, err) - continue - } - - continue - } - - // sar not expected - if err != nil { - t.Fatalf("%s: unexpected error: %v", name, err) - } - - actual := &api.ImageStream{} - if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil { - t.Fatalf("%s: unexpected extraction error: %v", name, err) - } - if e, a := fmt.Sprintf("%s/other:latest", otherNamespace), actual.Status.Tags["other"].Items[0].DockerImageReference; e != a { - t.Errorf("%s: dockerImageReference: expected %q, got %q", name, e, a) - } - } -} - -func TestUpdateImageStreamTags(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/test")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "default"}, - Spec: api.ImageStreamSpec{ - Tags: map[string]api.TagReference{ - "another": { - From: &kapi.ObjectReference{ - Kind: "ImageStreamTag", - Name: "test:another", - }, - }, - api.DefaultImageTag: { - From: &kapi.ObjectReference{ - Kind: "ImageStreamTag", - Name: "test:latest", - }, - }, - }, - }, - Status: api.ImageStreamStatus{ - DockerImageRepository: "registry.default.local/default/test", - Tags: map[string]api.TagEventList{ - api.DefaultImageTag: { - Items: []api.TagEvent{ - { - DockerImageReference: "registry.default.local/default/test@sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", - Image: "sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", - }, - }, - }, - }, - }, - }), - ModifiedIndex: 1, - }, - }, - } - - _, _, storage := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - stream := &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{ - Namespace: "default", - Name: "test", - ResourceVersion: "1", - }, - Spec: api.ImageStreamSpec{ - Tags: map[string]api.TagReference{ - "another": { - From: &kapi.ObjectReference{ - Kind: "ImageStreamTag", - Name: "test:another", - }, - }, - api.DefaultImageTag: { - From: &kapi.ObjectReference{ - Kind: "ImageStreamTag", - Name: "test:latest", - }, - }, - }, - }, - Status: api.ImageStreamStatus{ - DockerImageRepository: "registry.default.local/default/test", - Tags: map[string]api.TagEventList{ - api.DefaultImageTag: { - Items: []api.TagEvent{ - { - DockerImageReference: "registry.default.local/default/test@sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", - Image: "sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", - }, - }, - }, - }, - }, - } - - delete(stream.Spec.Tags, api.DefaultImageTag) - delete(stream.Status.Tags, api.DefaultImageTag) - - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - - obj, created, err := storage.Update(ctx, stream) - if err != nil { - t.Fatalf("Unexpected non-nil error: %#v", err) - } - if created { - t.Fatal("Unexpected stream creation") - } - updated, ok := obj.(*api.ImageStream) - if !ok { - t.Errorf("Expected image stream, got %#v", obj) - } - if _, ok := updated.Spec.Tags[api.DefaultImageTag]; ok { - t.Errorf("Expected deleted spec tag: %s", api.DefaultImageTag) - } - if _, ok := updated.Status.Tags[api.DefaultImageTag]; ok { - t.Errorf("Expected deleted status tag: %s", api.DefaultImageTag) - } -} - -func TestDeleteImageStream(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "default"}, - }), - ModifiedIndex: 2, - }, - }, - } - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - obj, err := storage.Delete(kapi.NewDefaultContext(), "foo", nil) - if err != nil { - t.Fatalf("Unexpected non-nil error: %#v", err) - } - status, ok := obj.(*unversioned.Status) - if !ok { - t.Fatalf("Expected status, got %#v", obj) - } - if status.Status != unversioned.StatusSuccess { - t.Errorf("Expected status=success, got %#v", status) - } -} - -func TestUpdateImageStreamConflictingNamespace(t *testing.T) { - fakeEtcdClient, helper := newHelper(t) - fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/legal-name/bar")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "default"}, - }), - ModifiedIndex: 2, - }, - }, - } - storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - - ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "legal-name"), &fakeUser{}) - obj, created, err := storage.Update(ctx, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "some-value", ResourceVersion: "2"}, - }) - - if obj != nil || created { - t.Error("Expected a nil obj, but we got a value") - } - - checkExpectedNamespaceError(t, err) -} - -func checkExpectedNamespaceError(t *testing.T, err error) { - expectedError := "the namespace of the provided object does not match the namespace sent on the request" - if err == nil { - t.Fatalf("Expected '" + expectedError + "', but we didn't get one") - } - if !strings.Contains(err.Error(), expectedError) { - t.Errorf("Expected '"+expectedError+"' error, got '%v'", err.Error()) - } - -} - -/* -func TestEtcdListImagesStreamsEmpty(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - key := makeTestDefaultImageStreamsListKey() - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{}, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(repos.Items) != 0 { - t.Errorf("Unexpected image streams list: %#v", repos) - } -} - -func TestEtcdListImageStreamsError(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - key := makeTestDefaultImageStreamsListKey() - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: fmt.Errorf("some error"), - } - registry := NewTestEtcd(fakeClient) - repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything()) - if err == nil { - t.Error("unexpected nil error") - } - - if repos != nil { - t.Errorf("Unexpected non-nil repos: %#v", repos) - } -} - -func TestEtcdListImageStreamsEverything(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - key := makeTestDefaultImageStreamsListKey() - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}}), - }, - }, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - registry.defaultRegistry = testDefaultRegistry - repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(repos.Items) != 2 || repos.Items[0].Name != "foo" || repos.Items[1].Name != "bar" || repos.Items[1].Status.DockerImageRepository != "test/default/bar" { - t.Errorf("Unexpected images list: %#v", repos) - } -} - -func TestEtcdListImageStreamsFiltered(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - key := makeTestDefaultImageStreamsListKey() - fakeClient.Data[key] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"env": "prod"}, - }, - }), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{ - Name: "bar", - Labels: map[string]string{"env": "dev"}, - }, - }), - }, - }, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.SelectorFromSet(labels.Set{"env": "dev"})) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(repos.Items) != 1 || repos.Items[0].Name != "bar" { - t.Errorf("Unexpected repos list: %#v", repos) - } -} - -func TestEtcdGetImageStream(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set(makeTestDefaultImageStreamsKey("foo"), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) - registry := NewTestEtcd(fakeClient) - stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if stream.Name != "foo" { - t.Errorf("Unexpected stream: %#v", stream) - } -} - -func TestEtcdGetImageStreamNotFound(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: tools.EtcdErrorNotFound, - } - registry := NewTestEtcd(fakeClient) - stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo") - if err == nil { - t.Errorf("Unexpected non-error.") - } - if stream != nil { - t.Errorf("Unexpected non-nil stream: %#v", stream) - } -} - -func TestEtcdCreateImageStream(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.TestIndex = true - fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: nil, - }, - E: tools.EtcdErrorNotFound, - } - registry := NewTestEtcd(fakeClient) - err := registry.CreateImageStream(kapi.NewDefaultContext(), &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"a": "b"}, - }, - DockerImageRepository: "c/d", - Tags: map[string]string{"t1": "v1"}, - }) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - resp, err := fakeClient.Get(makeTestDefaultImageStreamsKey("foo"), false, false) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - var stream api.ImageStream - err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &stream) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if stream.Name != "foo" { - t.Errorf("Unexpected stream: %#v %s", stream, resp.Node.Value) - } - - if len(stream.Labels) != 1 || stream.Labels["a"] != "b" { - t.Errorf("Unexpected labels: %#v", stream.Labels) - } - - if stream.DockerImageRepository != "c/d" { - t.Errorf("Unexpected docker image stream: %s", stream.DockerImageRepository) - } - - if len(stream.Tags) != 1 || stream.Tags["t1"] != "v1" { - t.Errorf("Unexpected tags: %#v", stream.Tags) - } -} - -func TestEtcdCreateImageStreamAlreadyExists(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - err := registry.CreateImageStream(kapi.NewDefaultContext(), &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - }, - }) - if err == nil { - t.Error("Unexpected non-error") - } - if !errors.IsAlreadyExists(err) { - t.Errorf("Expected 'already exists' error, got %#v", err) - } -} - -func TestEtcdUpdateImageStream(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.TestIndex = true - - resp, _ := fakeClient.Set(makeTestDefaultImageStreamsKey("foo"), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) - registry := NewTestEtcd(fakeClient) - err := registry.UpdateImageStreamSpec(kapi.NewDefaultContext(), &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{Name: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)}, - DockerImageRepository: "some/stream", - }) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo") - if stream.DockerImageRepository != "some/stream" { - t.Errorf("Unexpected stream: %#v", stream) - } -} - -func TestEtcdDeleteImageStreamNotFound(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Err = tools.EtcdErrorNotFound - registry := NewTestEtcd(fakeClient) - err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo") - if err == nil { - t.Error("Unexpected non-error") - } - if !errors.IsNotFound(err) { - t.Errorf("Expected 'not found' error, got %#v", err) - } -} - -func TestEtcdDeleteImageStreamError(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Err = fmt.Errorf("Some error") - registry := NewTestEtcd(fakeClient) - err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo") - if err == nil { - t.Error("Unexpected non-error") - } -} - -func TestEtcdDeleteImageStreamOK(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - registry := NewTestEtcd(fakeClient) - key := makeTestDefaultImageStreamsListKey() + "/foo" - err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo") - if err != nil { - t.Errorf("Unexpected error: %#v", err) - } - if len(fakeClient.DeletedKeys) != 1 { - t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys) - } else if fakeClient.DeletedKeys[0] != key { - t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key) - } -} - -func TestEtcdWatchImageStreams(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - registry := NewTestEtcd(fakeClient) - - var tests = []struct { - label labels.Selector - field labels.Selector - repos []*api.ImageStream - expected []bool - }{ - // want everything - { - labels.Everything(), - labels.Everything(), - []*api.ImageStream{ - {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"l1": "v1"}}, DockerImageRepository: "r1"}, - {ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, - {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"l3": "v3"}}, DockerImageRepository: "r3"}, - }, - []bool{ - true, - true, - true, - }, - }, - // want name=foo - { - labels.Everything(), - labels.SelectorFromSet(labels.Set{"name": "foo"}), - []*api.ImageStream{ - {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"l1": "v1"}}, DockerImageRepository: "r1"}, - {ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, - {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"l3": "v3"}}, DockerImageRepository: "r3"}, - }, - []bool{ - false, - true, - false, - }, - }, - // want label color:blue - { - labels.SelectorFromSet(labels.Set{"color": "blue"}), - labels.Everything(), - []*api.ImageStream{ - {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r1"}, - {ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, - {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r3"}, - }, - []bool{ - true, - false, - true, - }, - }, - // want name=foo, label color:blue, dockerImageStream=r1 - { - labels.SelectorFromSet(labels.Set{"color": "blue"}), - labels.SelectorFromSet(labels.Set{"dockerImageStream": "r1", "name": "foo"}), - []*api.ImageStream{ - {ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r1"}, - {ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, - {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r3"}, - }, - []bool{ - true, - false, - false, - }, - }, - } - - for _, tt := range tests { - watching, err := registry.WatchImageStreams(kapi.NewDefaultContext(), tt.label, tt.field, "1") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - fakeClient.WaitForWatchCompletion() - - for testIndex, stream := range tt.repos { - // Set this value to avoid duplication in tests - stream.Status.DockerImageRepository = stream.DockerImageRepository - repoBytes, _ := latest.Codec.Encode(stream) - fakeClient.WatchResponse <- &etcd.Response{ - Action: "set", - Node: &etcd.Node{ - Value: string(repoBytes), - }, - } - - select { - case event, ok := <-watching.ResultChan(): - if !ok { - t.Errorf("watching channel should be open") - } - if !tt.expected[testIndex] { - t.Errorf("unexpected imageStream returned from watch: %#v", event.Object) - } - if e, a := watch.Added, event.Type; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - if e, a := stream, event.Object; !reflect.DeepEqual(e, a) { - t.Errorf("Expected %#v, got %#v", e, a) - } - case <-time.After(50 * time.Millisecond): - if tt.expected[testIndex] { - t.Errorf("Expected imageStream %#v to be returned from watch", stream) - } - } - } - - select { - case _, ok := <-watching.ResultChan(): - if !ok { - t.Errorf("watching channel should be open") - } - default: - } - - fakeClient.WatchInjectError <- nil - if _, ok := <-watching.ResultChan(); ok { - t.Errorf("watching channel should be closed") - } - watching.Stop() - } -} - -func TestEtcdCreateImageStreamFailsWithoutNamespace(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.TestIndex = true - registry := NewTestEtcd(fakeClient) - err := registry.CreateImageStream(kapi.NewContext(), &api.ImageStream{ - ObjectMeta: kapi.ObjectMeta{ - Name: "foo", - }, - }) - - if err == nil { - t.Errorf("expected error that namespace was missing from context") - } -} - -func TestEtcdListImageStreamsInDifferentNamespaces(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - namespaceAlfa := kapi.WithNamespace(kapi.NewContext(), "alfa") - namespaceBravo := kapi.WithNamespace(kapi.NewContext(), "bravo") - fakeClient.Data["/imagestreams/alfa"] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo1"}}), - }, - }, - }, - }, - E: nil, - } - fakeClient.Data["/imagestreams/bravo"] = tools.EtcdResponseWithError{ - R: &etcd.Response{ - Node: &etcd.Node{ - Nodes: []*etcd.Node{ - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo2"}}), - }, - { - Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar2"}}), - }, - }, - }, - }, - E: nil, - } - registry := NewTestEtcd(fakeClient) - - imageStreamsAlfa, err := registry.ListImageStreams(namespaceAlfa, labels.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(imageStreamsAlfa.Items) != 1 || imageStreamsAlfa.Items[0].Name != "foo1" { - t.Errorf("Unexpected imageStream list: %#v", imageStreamsAlfa) - } - - imageStreamsBravo, err := registry.ListImageStreams(namespaceBravo, labels.Everything()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(imageStreamsBravo.Items) != 2 || imageStreamsBravo.Items[0].Name != "foo2" || imageStreamsBravo.Items[1].Name != "bar2" { - t.Errorf("Unexpected imageStream list: %#v", imageStreamsBravo) - } -} - -func TestEtcdGetImageStreamInDifferentNamespaces(t *testing.T) { - fakeClient := tools.NewFakeEtcdClient(t) - namespaceAlfa := kapi.WithNamespace(kapi.NewContext(), "alfa") - namespaceBravo := kapi.WithNamespace(kapi.NewContext(), "bravo") - fakeClient.Set("/imagestreams/alfa/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) - fakeClient.Set("/imagestreams/bravo/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) - registry := NewTestEtcd(fakeClient) - - alfaFoo, err := registry.GetImageStream(namespaceAlfa, "foo") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if alfaFoo == nil || alfaFoo.Name != "foo" { - t.Errorf("Unexpected deployment: %#v", alfaFoo) - } - - bravoFoo, err := registry.GetImageStream(namespaceBravo, "foo") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if bravoFoo == nil || bravoFoo.Name != "foo" { - t.Errorf("Unexpected deployment: %#v", bravoFoo) - } -} -*/ -type fakeStrategy struct { - imagestream.Strategy -} - -func (fakeStrategy) PrepareForCreate(obj runtime.Object) { - stream := obj.(*api.ImageStream) - stream.Annotations = map[string]string{"test": "PrepareForCreate"} -} - -func (fakeStrategy) PrepareForUpdate(obj, old runtime.Object) { - stream := obj.(*api.ImageStream) - stream.Annotations["test"] = "PrepareForUpdate" -} - -func TestStrategyPrepareMethods(t *testing.T) { - _, helper := newHelper(t) - storage, _, _ := NewREST(helper, testDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) - stream := validNewStream() - strategy := fakeStrategy{imagestream.NewStrategy(testDefaultRegistry, &fakeSubjectAccessReviewRegistry{})} - - storage.store.CreateStrategy = strategy - storage.store.UpdateStrategy = strategy - - ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) - obj, err := storage.Create(ctx, stream) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - updatedStream := obj.(*api.ImageStream) - if updatedStream.Annotations["test"] != "PrepareForCreate" { - t.Errorf("Expected PrepareForCreate annotation") - } - - obj, _, err = storage.Update(ctx, updatedStream) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - updatedStream = obj.(*api.ImageStream) - if updatedStream.Annotations["test"] != "PrepareForUpdate" { - t.Errorf("Expected PrepareForUpdate annotation") - } -} +// func TestCreateImageStreamOK(t *testing.T) { +// _, helper := newStorage(t) +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// stream := &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}} +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) +// _, err := storage.Create(ctx, stream) +// if err != nil { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } + +// actual := &api.ImageStream{} +// if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil { +// t.Fatalf("unexpected extraction error: %v", err) +// } +// if actual.Name != stream.Name { +// t.Errorf("unexpected stream: %#v", actual) +// } +// if len(actual.UID) == 0 { +// t.Errorf("expected stream UID to be set: %#v", actual) +// } +// if stream.CreationTimestamp.IsZero() { +// t.Error("Unexpected zero CreationTimestamp") +// } +// if stream.Spec.DockerImageRepository != "" { +// t.Errorf("unexpected stream: %#v", stream) +// } +// } + +// func TestCreateImageStreamSpecTagsFromSet(t *testing.T) { +// tests := map[string]struct { +// otherNamespace string +// sarExpected bool +// sarAllowed bool +// }{ +// "same namespace (blank), no sar": { +// otherNamespace: "", +// sarExpected: false, +// }, +// "same namespace (set), no sar": { +// otherNamespace: "default", +// sarExpected: false, +// }, +// "different namespace, sar allowed": { +// otherNamespace: "otherns", +// sarExpected: true, +// sarAllowed: true, +// }, +// "different namespace, sar denied": { +// otherNamespace: "otherns", +// sarExpected: true, +// sarAllowed: false, +// }, +// } +// for name, test := range tests { +// fakeEtcdClient, helper := newStorage(t) +// sarRegistry := &fakeSubjectAccessReviewRegistry{ +// allow: test.sarAllowed, +// } +// storage, _, _ := NewREST(helper, noDefaultRegistry, sarRegistry) + +// otherNamespace := test.otherNamespace +// if len(otherNamespace) == 0 { +// otherNamespace = "default" +// } +// fakeEtcdClient.Data[fmt.Sprintf(etcdtest.AddPrefix("/imagestreams/%s/other"), otherNamespace)] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "other", Namespace: otherNamespace}, +// Status: api.ImageStreamStatus{ +// Tags: map[string]api.TagEventList{ +// "latest": { +// Items: []api.TagEvent{ +// { +// DockerImageReference: fmt.Sprintf("%s/other:latest", otherNamespace), +// }, +// }, +// }, +// }, +// }, +// }), +// ModifiedIndex: 1, +// }, +// }, +// } + +// stream := &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "foo"}, +// Spec: api.ImageStreamSpec{ +// Tags: map[string]api.TagReference{ +// "other": { +// From: &kapi.ObjectReference{ +// Kind: "ImageStreamTag", +// Namespace: test.otherNamespace, +// Name: "other:latest", +// }, +// }, +// }, +// }, +// } +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) +// _, err := storage.Create(ctx, stream) +// if test.sarExpected { +// if sarRegistry.request == nil { +// t.Errorf("%s: expected sar request", name) +// continue +// } +// if e, a := test.sarAllowed, err == nil; e != a { +// t.Errorf("%s: expected sarAllowed=%t, got error %t: %v", name, e, a, err) +// continue +// } + +// continue +// } + +// // sar not expected +// if err != nil { +// t.Fatalf("%s: unexpected error: %v", name, err) +// } + +// actual := &api.ImageStream{} +// if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil { +// t.Fatalf("%s: unexpected extraction error: %v", name, err) +// } +// if e, a := fmt.Sprintf("%s/other:latest", otherNamespace), actual.Status.Tags["other"].Items[0].DockerImageReference; e != a { +// t.Errorf("%s: dockerImageReference: expected %q, got %q", name, e, a) +// } +// } +// } + +// func TestCreateRegistryErrorSaving(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.Err = fmt.Errorf("foo") +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) +// _, err := storage.Create(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}) +// if err != fakeEtcdClient.Err { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } +// } + +// func TestUpdateImageStreamMissingID(t *testing.T) { +// _, helper := newStorage(t) +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// obj, created, err := storage.Update(kapi.NewDefaultContext(), &api.ImageStream{}) +// if obj != nil || created { +// t.Fatalf("Expected nil, got %v", obj) +// } +// if strings.Index(err.Error(), "Name parameter required") == -1 { +// t.Errorf("Expected 'Name parameter required' error, got %v", err) +// } +// } + +// func TestUpdateRegistryErrorSaving(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.Err = fmt.Errorf("foo") +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// _, created, err := storage.Update(kapi.NewDefaultContext(), &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}}) +// if err != fakeEtcdClient.Err || created { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } +// } + +// func TestUpdateImageStreamOK(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/bar")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "default"}, +// }), +// ModifiedIndex: 2, +// }, +// }, +// } +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) +// obj, created, err := storage.Update(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar", ResourceVersion: "1"}}) +// if !errors.IsConflict(err) { +// t.Fatalf("unexpected non-error: %v", err) +// } +// obj, created, err = storage.Update(ctx, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar", ResourceVersion: "2"}}) +// if err != nil || created { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } +// stream, ok := obj.(*api.ImageStream) +// if !ok { +// t.Errorf("Expected image stream, got %#v", obj) +// } +// if stream.Name != "bar" { +// t.Errorf("Unexpected stream returned: %#v", stream) +// } +// } + +// func TestUpdateImageStreamSpecTagsFromSet(t *testing.T) { +// tests := map[string]struct { +// otherNamespace string +// sarExpected bool +// sarAllowed bool +// }{ +// "same namespace (blank), no sar": { +// otherNamespace: "", +// sarExpected: false, +// }, +// "same namespace (set), no sar": { +// otherNamespace: "default", +// sarExpected: false, +// }, +// "different namespace, sar allowed": { +// otherNamespace: "otherns", +// sarExpected: true, +// sarAllowed: true, +// }, +// "different namespace, sar denied": { +// otherNamespace: "otherns", +// sarExpected: true, +// sarAllowed: false, +// }, +// } +// for name, test := range tests { +// fakeEtcdClient, helper := newStorage(t) +// sarRegistry := &fakeSubjectAccessReviewRegistry{ +// allow: test.sarAllowed, +// } +// storage, _, _ := NewREST(helper, noDefaultRegistry, sarRegistry) + +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/foo")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "default"}, +// }), +// ModifiedIndex: 1, +// }, +// }, +// } + +// otherNamespace := test.otherNamespace +// if len(otherNamespace) == 0 { +// otherNamespace = "default" +// } +// fakeEtcdClient.Data[fmt.Sprintf(etcdtest.AddPrefix("/imagestreams/%s/other"), otherNamespace)] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "other", Namespace: otherNamespace}, +// Status: api.ImageStreamStatus{ +// Tags: map[string]api.TagEventList{ +// "latest": { +// Items: []api.TagEvent{ +// { +// DockerImageReference: fmt.Sprintf("%s/other:latest", otherNamespace), +// }, +// }, +// }, +// }, +// }, +// }), +// ModifiedIndex: 1, +// }, +// }, +// } + +// stream := &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "foo", ResourceVersion: "1"}, +// Spec: api.ImageStreamSpec{ +// Tags: map[string]api.TagReference{ +// "other": { +// From: &kapi.ObjectReference{ +// Kind: "ImageStreamTag", +// Namespace: test.otherNamespace, +// Name: "other:latest", +// }, +// }, +// }, +// }, +// } +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) +// _, _, err := storage.Update(ctx, stream) +// if test.sarExpected { +// if sarRegistry.request == nil { +// t.Errorf("%s: expected sar request", name) +// continue +// } +// if e, a := test.sarAllowed, err == nil; e != a { +// t.Errorf("%s: expected sarAllowed=%t, got error %t: %v", name, e, a, err) +// continue +// } + +// continue +// } + +// // sar not expected +// if err != nil { +// t.Fatalf("%s: unexpected error: %v", name, err) +// } + +// actual := &api.ImageStream{} +// if err := helper.Get(kapi.NewDefaultContext(), "/imagestreams/default/foo", actual, false); err != nil { +// t.Fatalf("%s: unexpected extraction error: %v", name, err) +// } +// if e, a := fmt.Sprintf("%s/other:latest", otherNamespace), actual.Status.Tags["other"].Items[0].DockerImageReference; e != a { +// t.Errorf("%s: dockerImageReference: expected %q, got %q", name, e, a) +// } +// } +// } + +// func TestUpdateImageStreamTags(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/test")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "default"}, +// Spec: api.ImageStreamSpec{ +// Tags: map[string]api.TagReference{ +// "another": { +// From: &kapi.ObjectReference{ +// Kind: "ImageStreamTag", +// Name: "test:another", +// }, +// }, +// api.DefaultImageTag: { +// From: &kapi.ObjectReference{ +// Kind: "ImageStreamTag", +// Name: "test:latest", +// }, +// }, +// }, +// }, +// Status: api.ImageStreamStatus{ +// DockerImageRepository: "registry.default.local/default/test", +// Tags: map[string]api.TagEventList{ +// api.DefaultImageTag: { +// Items: []api.TagEvent{ +// { +// DockerImageReference: "registry.default.local/default/test@sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", +// Image: "sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", +// }, +// }, +// }, +// }, +// }, +// }), +// ModifiedIndex: 1, +// }, +// }, +// } + +// _, _, storage := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// stream := &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{ +// Namespace: "default", +// Name: "test", +// ResourceVersion: "1", +// }, +// Spec: api.ImageStreamSpec{ +// Tags: map[string]api.TagReference{ +// "another": { +// From: &kapi.ObjectReference{ +// Kind: "ImageStreamTag", +// Name: "test:another", +// }, +// }, +// api.DefaultImageTag: { +// From: &kapi.ObjectReference{ +// Kind: "ImageStreamTag", +// Name: "test:latest", +// }, +// }, +// }, +// }, +// Status: api.ImageStreamStatus{ +// DockerImageRepository: "registry.default.local/default/test", +// Tags: map[string]api.TagEventList{ +// api.DefaultImageTag: { +// Items: []api.TagEvent{ +// { +// DockerImageReference: "registry.default.local/default/test@sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", +// Image: "sha256:381151ac5b7f775e8371e489f3479b84a4c004c90ceddb2ad80b6877215a892f", +// }, +// }, +// }, +// }, +// }, +// } + +// delete(stream.Spec.Tags, api.DefaultImageTag) +// delete(stream.Status.Tags, api.DefaultImageTag) + +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) + +// obj, created, err := storage.Update(ctx, stream) +// if err != nil { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } +// if created { +// t.Fatal("Unexpected stream creation") +// } +// updated, ok := obj.(*api.ImageStream) +// if !ok { +// t.Errorf("Expected image stream, got %#v", obj) +// } +// if _, ok := updated.Spec.Tags[api.DefaultImageTag]; ok { +// t.Errorf("Expected deleted spec tag: %s", api.DefaultImageTag) +// } +// if _, ok := updated.Status.Tags[api.DefaultImageTag]; ok { +// t.Errorf("Expected deleted status tag: %s", api.DefaultImageTag) +// } +// } + +// func TestDeleteImageStream(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/default/foo")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "foo", Namespace: "default"}, +// }), +// ModifiedIndex: 2, +// }, +// }, +// } +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// obj, err := storage.Delete(kapi.NewDefaultContext(), "foo", nil) +// if err != nil { +// t.Fatalf("Unexpected non-nil error: %#v", err) +// } +// status, ok := obj.(*unversioned.Status) +// if !ok { +// t.Fatalf("Expected status, got %#v", obj) +// } +// if status.Status != unversioned.StatusSuccess { +// t.Errorf("Expected status=success, got %#v", status) +// } +// } + +// func TestUpdateImageStreamConflictingNamespace(t *testing.T) { +// fakeEtcdClient, helper := newStorage(t) +// fakeEtcdClient.Data[etcdtest.AddPrefix("/imagestreams/legal-name/bar")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "default"}, +// }), +// ModifiedIndex: 2, +// }, +// }, +// } +// storage, _, _ := NewREST(helper, noDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) + +// ctx := kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "legal-name"), &fakeUser{}) +// obj, created, err := storage.Update(ctx, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "bar", Namespace: "some-value", ResourceVersion: "2"}, +// }) + +// if obj != nil || created { +// t.Error("Expected a nil obj, but we got a value") +// } + +// checkExpectedNamespaceError(t, err) +// } + +// func checkExpectedNamespaceError(t *testing.T, err error) { +// expectedError := "the namespace of the provided object does not match the namespace sent on the request" +// if err == nil { +// t.Fatalf("Expected '" + expectedError + "', but we didn't get one") +// } +// if !strings.Contains(err.Error(), expectedError) { +// t.Errorf("Expected '"+expectedError+"' error, got '%v'", err.Error()) +// } + +// } + +// /* +// func TestEtcdListImagesStreamsEmpty(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// key := makeTestDefaultImageStreamsListKey() +// fakeClient.Data[key] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Nodes: []*etcd.Node{}, +// }, +// }, +// E: nil, +// } +// registry := NewTestEtcd(fakeClient) +// repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything()) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } + +// if len(repos.Items) != 0 { +// t.Errorf("Unexpected image streams list: %#v", repos) +// } +// } + +// func TestEtcdListImageStreamsError(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// key := makeTestDefaultImageStreamsListKey() +// fakeClient.Data[key] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: nil, +// }, +// E: fmt.Errorf("some error"), +// } +// registry := NewTestEtcd(fakeClient) +// repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything()) +// if err == nil { +// t.Error("unexpected nil error") +// } + +// if repos != nil { +// t.Errorf("Unexpected non-nil repos: %#v", repos) +// } +// } + +// func TestEtcdListImageStreamsEverything(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// key := makeTestDefaultImageStreamsListKey() +// fakeClient.Data[key] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Nodes: []*etcd.Node{ +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), +// }, +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar"}}), +// }, +// }, +// }, +// }, +// E: nil, +// } +// registry := NewTestEtcd(fakeClient) +// registry.defaultRegistry = testDefaultRegistry +// repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.Everything()) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } + +// if len(repos.Items) != 2 || repos.Items[0].Name != "foo" || repos.Items[1].Name != "bar" || repos.Items[1].Status.DockerImageRepository != "test/default/bar" { +// t.Errorf("Unexpected images list: %#v", repos) +// } +// } + +// func TestEtcdListImageStreamsFiltered(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// key := makeTestDefaultImageStreamsListKey() +// fakeClient.Data[key] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Nodes: []*etcd.Node{ +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{ +// Name: "foo", +// Labels: map[string]string{"env": "prod"}, +// }, +// }), +// }, +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{ +// Name: "bar", +// Labels: map[string]string{"env": "dev"}, +// }, +// }), +// }, +// }, +// }, +// }, +// E: nil, +// } +// registry := NewTestEtcd(fakeClient) +// repos, err := registry.ListImageStreams(kapi.NewDefaultContext(), labels.SelectorFromSet(labels.Set{"env": "dev"})) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } + +// if len(repos.Items) != 1 || repos.Items[0].Name != "bar" { +// t.Errorf("Unexpected repos list: %#v", repos) +// } +// } + +// func TestEtcdGetImageStream(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.Set(makeTestDefaultImageStreamsKey("foo"), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) +// registry := NewTestEtcd(fakeClient) +// stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo") +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } + +// if stream.Name != "foo" { +// t.Errorf("Unexpected stream: %#v", stream) +// } +// } + +// func TestEtcdGetImageStreamNotFound(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: nil, +// }, +// E: tools.EtcdErrorNotFound, +// } +// registry := NewTestEtcd(fakeClient) +// stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo") +// if err == nil { +// t.Errorf("Unexpected non-error.") +// } +// if stream != nil { +// t.Errorf("Unexpected non-nil stream: %#v", stream) +// } +// } + +// func TestEtcdCreateImageStream(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.TestIndex = true +// fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: nil, +// }, +// E: tools.EtcdErrorNotFound, +// } +// registry := NewTestEtcd(fakeClient) +// err := registry.CreateImageStream(kapi.NewDefaultContext(), &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{ +// Name: "foo", +// Labels: map[string]string{"a": "b"}, +// }, +// DockerImageRepository: "c/d", +// Tags: map[string]string{"t1": "v1"}, +// }) +// if err != nil { +// t.Fatalf("unexpected error: %v", err) +// } + +// resp, err := fakeClient.Get(makeTestDefaultImageStreamsKey("foo"), false, false) +// if err != nil { +// t.Fatalf("Unexpected error %v", err) +// } +// var stream api.ImageStream +// err = latest.Codec.DecodeInto([]byte(resp.Node.Value), &stream) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } + +// if stream.Name != "foo" { +// t.Errorf("Unexpected stream: %#v %s", stream, resp.Node.Value) +// } + +// if len(stream.Labels) != 1 || stream.Labels["a"] != "b" { +// t.Errorf("Unexpected labels: %#v", stream.Labels) +// } + +// if stream.DockerImageRepository != "c/d" { +// t.Errorf("Unexpected docker image stream: %s", stream.DockerImageRepository) +// } + +// if len(stream.Tags) != 1 || stream.Tags["t1"] != "v1" { +// t.Errorf("Unexpected tags: %#v", stream.Tags) +// } +// } + +// func TestEtcdCreateImageStreamAlreadyExists(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.Data[makeTestDefaultImageStreamsKey("foo")] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), +// }, +// }, +// E: nil, +// } +// registry := NewTestEtcd(fakeClient) +// err := registry.CreateImageStream(kapi.NewDefaultContext(), &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{ +// Name: "foo", +// }, +// }) +// if err == nil { +// t.Error("Unexpected non-error") +// } +// if !errors.IsAlreadyExists(err) { +// t.Errorf("Expected 'already exists' error, got %#v", err) +// } +// } + +// func TestEtcdUpdateImageStream(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.TestIndex = true + +// resp, _ := fakeClient.Set(makeTestDefaultImageStreamsKey("foo"), runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) +// registry := NewTestEtcd(fakeClient) +// err := registry.UpdateImageStreamSpec(kapi.NewDefaultContext(), &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{Name: "foo", ResourceVersion: strconv.FormatUint(resp.Node.ModifiedIndex, 10)}, +// DockerImageRepository: "some/stream", +// }) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } + +// stream, err := registry.GetImageStream(kapi.NewDefaultContext(), "foo") +// if stream.DockerImageRepository != "some/stream" { +// t.Errorf("Unexpected stream: %#v", stream) +// } +// } + +// func TestEtcdDeleteImageStreamNotFound(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.Err = tools.EtcdErrorNotFound +// registry := NewTestEtcd(fakeClient) +// err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo") +// if err == nil { +// t.Error("Unexpected non-error") +// } +// if !errors.IsNotFound(err) { +// t.Errorf("Expected 'not found' error, got %#v", err) +// } +// } + +// func TestEtcdDeleteImageStreamError(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.Err = fmt.Errorf("Some error") +// registry := NewTestEtcd(fakeClient) +// err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo") +// if err == nil { +// t.Error("Unexpected non-error") +// } +// } + +// func TestEtcdDeleteImageStreamOK(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// registry := NewTestEtcd(fakeClient) +// key := makeTestDefaultImageStreamsListKey() + "/foo" +// err := registry.DeleteImageStream(kapi.NewDefaultContext(), "foo") +// if err != nil { +// t.Errorf("Unexpected error: %#v", err) +// } +// if len(fakeClient.DeletedKeys) != 1 { +// t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys) +// } else if fakeClient.DeletedKeys[0] != key { +// t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key) +// } +// } + +// func TestEtcdWatchImageStreams(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// registry := NewTestEtcd(fakeClient) + +// var tests = []struct { +// label labels.Selector +// field labels.Selector +// repos []*api.ImageStream +// expected []bool +// }{ +// // want everything +// { +// labels.Everything(), +// labels.Everything(), +// []*api.ImageStream{ +// {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"l1": "v1"}}, DockerImageRepository: "r1"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"l3": "v3"}}, DockerImageRepository: "r3"}, +// }, +// []bool{ +// true, +// true, +// true, +// }, +// }, +// // want name=foo +// { +// labels.Everything(), +// labels.SelectorFromSet(labels.Set{"name": "foo"}), +// []*api.ImageStream{ +// {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"l1": "v1"}}, DockerImageRepository: "r1"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"l3": "v3"}}, DockerImageRepository: "r3"}, +// }, +// []bool{ +// false, +// true, +// false, +// }, +// }, +// // want label color:blue +// { +// labels.SelectorFromSet(labels.Set{"color": "blue"}), +// labels.Everything(), +// []*api.ImageStream{ +// {ObjectMeta: kapi.ObjectMeta{Name: "a", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r1"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r3"}, +// }, +// []bool{ +// true, +// false, +// true, +// }, +// }, +// // want name=foo, label color:blue, dockerImageStream=r1 +// { +// labels.SelectorFromSet(labels.Set{"color": "blue"}), +// labels.SelectorFromSet(labels.Set{"dockerImageStream": "r1", "name": "foo"}), +// []*api.ImageStream{ +// {ObjectMeta: kapi.ObjectMeta{Name: "foo", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r1"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "b", Labels: labels.Set{"l2": "v2"}}, DockerImageRepository: "r2"}, +// {ObjectMeta: kapi.ObjectMeta{Name: "c", Labels: labels.Set{"color": "blue"}}, DockerImageRepository: "r3"}, +// }, +// []bool{ +// true, +// false, +// false, +// }, +// }, +// } + +// for _, tt := range tests { +// watching, err := registry.WatchImageStreams(kapi.NewDefaultContext(), tt.label, tt.field, "1") +// if err != nil { +// t.Fatalf("unexpected error: %v", err) +// } +// fakeClient.WaitForWatchCompletion() + +// for testIndex, stream := range tt.repos { +// // Set this value to avoid duplication in tests +// stream.Status.DockerImageRepository = stream.DockerImageRepository +// repoBytes, _ := latest.Codec.Encode(stream) +// fakeClient.WatchResponse <- &etcd.Response{ +// Action: "set", +// Node: &etcd.Node{ +// Value: string(repoBytes), +// }, +// } + +// select { +// case event, ok := <-watching.ResultChan(): +// if !ok { +// t.Errorf("watching channel should be open") +// } +// if !tt.expected[testIndex] { +// t.Errorf("unexpected imageStream returned from watch: %#v", event.Object) +// } +// if e, a := watch.Added, event.Type; e != a { +// t.Errorf("Expected %v, got %v", e, a) +// } +// if e, a := stream, event.Object; !reflect.DeepEqual(e, a) { +// t.Errorf("Expected %#v, got %#v", e, a) +// } +// case <-time.After(50 * time.Millisecond): +// if tt.expected[testIndex] { +// t.Errorf("Expected imageStream %#v to be returned from watch", stream) +// } +// } +// } + +// select { +// case _, ok := <-watching.ResultChan(): +// if !ok { +// t.Errorf("watching channel should be open") +// } +// default: +// } + +// fakeClient.WatchInjectError <- nil +// if _, ok := <-watching.ResultChan(); ok { +// t.Errorf("watching channel should be closed") +// } +// watching.Stop() +// } +// } + +// func TestEtcdCreateImageStreamFailsWithoutNamespace(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// fakeClient.TestIndex = true +// registry := NewTestEtcd(fakeClient) +// err := registry.CreateImageStream(kapi.NewContext(), &api.ImageStream{ +// ObjectMeta: kapi.ObjectMeta{ +// Name: "foo", +// }, +// }) + +// if err == nil { +// t.Errorf("expected error that namespace was missing from context") +// } +// } + +// func TestEtcdListImageStreamsInDifferentNamespaces(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// namespaceAlfa := kapi.WithNamespace(kapi.NewContext(), "alfa") +// namespaceBravo := kapi.WithNamespace(kapi.NewContext(), "bravo") +// fakeClient.Data["/imagestreams/alfa"] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Nodes: []*etcd.Node{ +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo1"}}), +// }, +// }, +// }, +// }, +// E: nil, +// } +// fakeClient.Data["/imagestreams/bravo"] = tools.EtcdResponseWithError{ +// R: &etcd.Response{ +// Node: &etcd.Node{ +// Nodes: []*etcd.Node{ +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo2"}}), +// }, +// { +// Value: runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "bar2"}}), +// }, +// }, +// }, +// }, +// E: nil, +// } +// registry := NewTestEtcd(fakeClient) + +// imageStreamsAlfa, err := registry.ListImageStreams(namespaceAlfa, labels.Everything()) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } +// if len(imageStreamsAlfa.Items) != 1 || imageStreamsAlfa.Items[0].Name != "foo1" { +// t.Errorf("Unexpected imageStream list: %#v", imageStreamsAlfa) +// } + +// imageStreamsBravo, err := registry.ListImageStreams(namespaceBravo, labels.Everything()) +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } +// if len(imageStreamsBravo.Items) != 2 || imageStreamsBravo.Items[0].Name != "foo2" || imageStreamsBravo.Items[1].Name != "bar2" { +// t.Errorf("Unexpected imageStream list: %#v", imageStreamsBravo) +// } +// } + +// func TestEtcdGetImageStreamInDifferentNamespaces(t *testing.T) { +// fakeClient := tools.NewFakeEtcdClient(t) +// namespaceAlfa := kapi.WithNamespace(kapi.NewContext(), "alfa") +// namespaceBravo := kapi.WithNamespace(kapi.NewContext(), "bravo") +// fakeClient.Set("/imagestreams/alfa/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) +// fakeClient.Set("/imagestreams/bravo/foo", runtime.EncodeOrDie(latest.Codec, &api.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "foo"}}), 0) +// registry := NewTestEtcd(fakeClient) + +// alfaFoo, err := registry.GetImageStream(namespaceAlfa, "foo") +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } +// if alfaFoo == nil || alfaFoo.Name != "foo" { +// t.Errorf("Unexpected deployment: %#v", alfaFoo) +// } + +// bravoFoo, err := registry.GetImageStream(namespaceBravo, "foo") +// if err != nil { +// t.Errorf("unexpected error: %v", err) +// } +// if bravoFoo == nil || bravoFoo.Name != "foo" { +// t.Errorf("Unexpected deployment: %#v", bravoFoo) +// } +// } +// */ +// type fakeStrategy struct { +// imagestream.Strategy +// } + +// func (fakeStrategy) PrepareForCreate(obj runtime.Object) { +// stream := obj.(*api.ImageStream) +// stream.Annotations = map[string]string{"test": "PrepareForCreate"} +// } + +// func (fakeStrategy) PrepareForUpdate(obj, old runtime.Object) { +// stream := obj.(*api.ImageStream) +// stream.Annotations["test"] = "PrepareForUpdate" +// } + +// func TestStrategyPrepareMethods(t *testing.T) { +// _, helper := newStorage(t) +// storage, _, _ := NewREST(helper, testDefaultRegistry, &fakeSubjectAccessReviewRegistry{}) +// stream := validNewStream() +// strategy := fakeStrategy{imagestream.NewStrategy(testDefaultRegistry, &fakeSubjectAccessReviewRegistry{})} + +// storage.store.CreateStrategy = strategy +// storage.store.UpdateStrategy = strategy + +// ctx := kapi.WithUser(kapi.NewDefaultContext(), &fakeUser{}) +// obj, err := storage.Create(ctx, stream) +// if err != nil { +// t.Fatalf("Unexpected error: %v", err) +// } + +// updatedStream := obj.(*api.ImageStream) +// if updatedStream.Annotations["test"] != "PrepareForCreate" { +// t.Errorf("Expected PrepareForCreate annotation") +// } + +// obj, _, err = storage.Update(ctx, updatedStream) +// if err != nil { +// t.Errorf("Unexpected error: %v", err) +// } + +// updatedStream = obj.(*api.ImageStream) +// if updatedStream.Annotations["test"] != "PrepareForUpdate" { +// t.Errorf("Expected PrepareForUpdate annotation") +// } +// }