// NewREST returns a RESTStorage object that will work against events. func NewREST(opts generic.RESTOptions, ttl uint64) *REST { prefix := "/" + opts.ResourcePrefix // We explicitly do NOT do any decoration here - switching on Cacher // for events will lead to too high memory consumption. storageInterface, dFunc := generic.NewRawStorage(opts.StorageConfig) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Event{} }, NewListFunc: func() runtime.Object { return &api.EventList{} }, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Event).Name, nil }, PredicateFunc: event.MatchEvent, TTLFunc: func(runtime.Object, uint64, bool) (uint64, error) { return ttl, nil }, QualifiedResource: api.Resource("events"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: event.Strategy, UpdateStrategy: event.Strategy, DeleteStrategy: event.Strategy, Storage: storageInterface, DestroyFunc: dFunc, } return &REST{store} }
// NewREST returns a registry which will store ThirdPartyResource in the given helper func NewREST(opts generic.RESTOptions) *REST { prefix := "/" + opts.ResourcePrefix // We explicitly do NOT do any decoration here yet. storageInterface, dFunc := generic.NewRawStorage(opts.StorageConfig) store := ®istry.Store{ NewFunc: func() runtime.Object { return &extensions.ThirdPartyResource{} }, NewListFunc: func() runtime.Object { return &extensions.ThirdPartyResourceList{} }, KeyRootFunc: func(ctx api.Context) string { return prefix }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NoNamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*extensions.ThirdPartyResource).Name, nil }, PredicateFunc: thirdpartyresource.Matcher, QualifiedResource: extensions.Resource("thirdpartyresources"), EnableGarbageCollection: opts.EnableGarbageCollection, DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: thirdpartyresource.Strategy, UpdateStrategy: thirdpartyresource.Strategy, DeleteStrategy: thirdpartyresource.Strategy, Storage: storageInterface, DestroyFunc: dFunc, } return &REST{store} }
// NewREST returns a registry which will store ThirdPartyResourceData in the given helper func NewREST(opts generic.RESTOptions, group, kind string) *REST { prefix := "/ThirdPartyResourceData/" + group + "/" + strings.ToLower(kind) + "s" // We explicitly do NOT do any decoration here yet. storageInterface, dFunc := generic.NewRawStorage(opts.StorageConfig) store := ®istry.Store{ NewFunc: func() runtime.Object { return &extensions.ThirdPartyResourceData{} }, NewListFunc: func() runtime.Object { return &extensions.ThirdPartyResourceDataList{} }, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*extensions.ThirdPartyResourceData).Name, nil }, PredicateFunc: thirdpartyresourcedata.Matcher, QualifiedResource: extensions.Resource("thirdpartyresourcedatas"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: thirdpartyresourcedata.Strategy, UpdateStrategy: thirdpartyresourcedata.Strategy, DeleteStrategy: thirdpartyresourcedata.Strategy, Storage: storageInterface, DestroyFunc: dFunc, } return &REST{ Store: store, kind: kind, } }
// Creates a cacher based given storageConfig. func StorageWithCacher( storageConfig *storagebackend.Config, capacity int, objectType runtime.Object, resourcePrefix string, keyFunc func(obj runtime.Object) (string, error), newListFunc func() runtime.Object, getAttrsFunc storage.AttrFunc, triggerFunc storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) { s, d := generic.NewRawStorage(storageConfig) // TODO: we would change this later to make storage always have cacher and hide low level KV layer inside. // Currently it has two layers of same storage interface -- cacher and low level kv. cacherConfig := storage.CacherConfig{ CacheCapacity: capacity, Storage: s, Versioner: etcdstorage.APIObjectVersioner{}, Type: objectType, ResourcePrefix: resourcePrefix, KeyFunc: keyFunc, NewListFunc: newListFunc, GetAttrsFunc: getAttrsFunc, TriggerPublisherFunc: triggerFunc, Codec: storageConfig.Codec, } cacher := storage.NewCacherFromConfig(cacherConfig) destroyFunc := func() { cacher.Stop() d() } return cacher, destroyFunc }
// Creates a cacher based given storageConfig. func StorageWithCacher( storageConfig *storagebackend.Config, capacity int, objectType runtime.Object, resourcePrefix string, scopeStrategy rest.NamespaceScopedStrategy, newListFunc func() runtime.Object, triggerFunc storage.TriggerPublisherFunc) storage.Interface { // TODO: we would change this later to make storage always have cacher and hide low level KV layer inside. // Currently it has two layers of same storage interface -- cacher and low level kv. cacherConfig := storage.CacherConfig{ CacheCapacity: capacity, Storage: generic.NewRawStorage(storageConfig), Versioner: etcdstorage.APIObjectVersioner{}, Type: objectType, ResourcePrefix: resourcePrefix, NewListFunc: newListFunc, TriggerPublisherFunc: triggerFunc, Codec: storageConfig.Codec, } if scopeStrategy.NamespaceScoped() { cacherConfig.KeyFunc = func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(resourcePrefix, obj) } } else { cacherConfig.KeyFunc = func(obj runtime.Object) (string, error) { return storage.NoNamespaceKeyFunc(resourcePrefix, obj) } } return storage.NewCacherFromConfig(cacherConfig) }
// NewEtcd returns an allocator that is backed by Etcd and can manage // persisting the snapshot state of allocation after each allocation is made. func NewEtcd(alloc allocator.Snapshottable, baseKey string, resource schema.GroupResource, config *storagebackend.Config) *Etcd { storage, _ := generic.NewRawStorage(config) return &Etcd{ alloc: alloc, storage: storage, baseKey: baseKey, resource: resource, } }
func testInstallThirdPartyAPIPostForVersion(t *testing.T, version string) { master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") defer server.Close() defer etcdserver.Terminate(t) inputObj := Foo{ ObjectMeta: api.ObjectMeta{ Name: "test", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", APIVersion: "company.com/" + version, }, SomeField: "test field", OtherField: 10, } data, err := json.Marshal(inputObj) if !assert.NoError(err) { return } resp, err := http.Post(server.URL+"/apis/company.com/"+version+"/namespaces/default/foos", "application/json", bytes.NewBuffer(data)) if !assert.NoError(err) { t.Fatalf("unexpected error: %v", err) } assert.Equal(http.StatusCreated, resp.StatusCode) item := Foo{} assert.NoError(decodeResponse(resp, &item)) // fill in fields set by the apiserver expectedObj := inputObj expectedObj.SelfLink = item.SelfLink expectedObj.ResourceVersion = item.ResourceVersion expectedObj.Namespace = item.Namespace expectedObj.UID = item.UID expectedObj.CreationTimestamp = item.CreationTimestamp if !assert.True(reflect.DeepEqual(item, expectedObj)) { t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) } thirdPartyObj := extensions.ThirdPartyResourceData{} s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) defer destroyFunc() err = s.Get(context.TODO(), etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/test"), &thirdPartyObj, false) if !assert.NoError(err) { t.FailNow() } item = Foo{} assert.NoError(json.Unmarshal(thirdPartyObj.Data, &item)) if !assert.True(reflect.DeepEqual(item, inputObj)) { t.Errorf("expected:\n%v\nsaw:\n%v\n", inputObj, item) } }
func newStorage(t *testing.T) (*ScaleREST, *etcdtesting.EtcdTestServer, storage.Interface, factory.DestroyFunc) { etcdStorage, server := registrytest.NewEtcdStorage(t, "") restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "controllers"} s, d := generic.NewRawStorage(etcdStorage) destroyFunc := func() { d() server.Terminate(t) } return NewStorage(restOptions).Scale, server, s, destroyFunc }
func newStorage(t *testing.T) (*etcdtesting.EtcdTestServer, ipallocator.Interface, allocator.Interface, storage.Interface) { etcdStorage, server := registrytest.NewEtcdStorage(t, "") _, cidr, err := net.ParseCIDR("192.168.1.0/24") if err != nil { t.Fatal(err) } var backing allocator.Interface storage := ipallocator.NewAllocatorCIDRRange(cidr, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) backing = mem etcd := allocatoretcd.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), etcdStorage) return etcd }) return server, storage, backing, generic.NewRawStorage(etcdStorage) }
func testInstallThirdPartyAPIGetVersion(t *testing.T, version string) { master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") defer server.Close() defer etcdserver.Terminate(t) expectedObj := Foo{ ObjectMeta: api.ObjectMeta{ Name: "test", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", APIVersion: version, }, SomeField: "test field", OtherField: 10, } s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) defer destroyFunc() if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { t.FailNow() return } resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusOK, resp.StatusCode) item := Foo{} assert.NoError(decodeResponse(resp, &item)) if !assert.False(reflect.DeepEqual(item, expectedObj)) { t.Errorf("expected objects to not be equal:\n%v\nsaw:\n%v\n", expectedObj, item) } // Fill in data that the apiserver injects expectedObj.SelfLink = item.SelfLink expectedObj.ResourceVersion = item.ResourceVersion if !assert.True(reflect.DeepEqual(item, expectedObj)) { t.Errorf("expected:\n%#v\nsaw:\n%#v\n", expectedObj, item) } }
func TestPodLogValidates(t *testing.T) { etcdStorage, _ := registrytest.NewEtcdStorage(t, "") store := ®istry.Store{ Storage: generic.NewRawStorage(etcdStorage), } logRest := &LogREST{Store: store, KubeletConn: nil} negativeOne := int64(-1) testCases := []*api.PodLogOptions{ {SinceSeconds: &negativeOne}, {TailLines: &negativeOne}, } for _, tc := range testCases { _, err := logRest.Get(api.NewDefaultContext(), "test", tc) if !errors.IsInvalid(err) { t.Fatalf("unexpected error: %v", err) } } }
func testInstallThirdPartyAPIDeleteVersion(t *testing.T, version string) { master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") defer server.Close() defer etcdserver.Terminate(t) expectedObj := Foo{ ObjectMeta: api.ObjectMeta{ Name: "test", Namespace: "default", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", }, SomeField: "test field", OtherField: 10, } s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) defer destroyFunc() if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { t.FailNow() return } resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusOK, resp.StatusCode) item := Foo{} assert.NoError(decodeResponse(resp, &item)) // Fill in fields set by the apiserver expectedObj.SelfLink = item.SelfLink expectedObj.ResourceVersion = item.ResourceVersion expectedObj.Namespace = item.Namespace if !assert.True(reflect.DeepEqual(item, expectedObj)) { t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) } resp, err = httpDelete(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusOK, resp.StatusCode) resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } assert.Equal(http.StatusNotFound, resp.StatusCode) expectedDeletedKey := etcdtest.AddPrefix("ThirdPartyResourceData/company.com/foos/default/test") thirdPartyObj := extensions.ThirdPartyResourceData{} err = s.Get(context.TODO(), expectedDeletedKey, &thirdPartyObj, false) if !storage.IsNotFound(err) { t.Errorf("expected deletion didn't happen: %v", err) } }
func testInstallThirdPartyAPIListVersion(t *testing.T, version string) { tests := []struct { items []Foo name string test string }{ { name: "foo.company.com", test: "null", }, { items: []Foo{}, name: "foo.company.com", test: "empty", }, { items: []Foo{}, name: "policy.company.com", test: "plurals", }, { items: []Foo{ { ObjectMeta: api.ObjectMeta{ Name: "test", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", APIVersion: version, }, SomeField: "test field", OtherField: 10, }, { ObjectMeta: api.ObjectMeta{ Name: "bar", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", APIVersion: version, }, SomeField: "test field another", OtherField: 20, }, }, name: "foo.company.com", test: "real list", }, } for _, test := range tests { func() { master, etcdserver, server, assert := initThirdParty(t, version, test.name) defer server.Close() defer etcdserver.Terminate(t) kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind( &extensions.ThirdPartyResource{ObjectMeta: api.ObjectMeta{Name: test.name}}) assert.NoError(err, test.test) plural, _ := meta.KindToResource(unversioned.GroupVersionKind{ Group: group, Version: version, Kind: kind, }) if test.items != nil { s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) defer destroyFunc() err := createThirdPartyList( s, fmt.Sprintf("/ThirdPartyResourceData/%s/%s/default", group, plural.Resource), test.items) if !assert.NoError(err, test.test) { return } } resp, err := http.Get( fmt.Sprintf("%s/apis/%s/%s/namespaces/default/%s", server.URL, group, version, plural.Resource)) if !assert.NoError(err, test.test) { return } defer resp.Body.Close() assert.Equal(http.StatusOK, resp.StatusCode, test.test) data, err := ioutil.ReadAll(resp.Body) assert.NoError(err, test.test) list := FooList{} if err = json.Unmarshal(data, &list); err != nil { assert.NoError(err, "unexpected error: %v %s", err, test.test) } if test.items == nil { if len(list.Items) != 0 { assert.NoError(err, "expected no items, saw: %v %s", err, list.Items, test.test) } return } if len(list.Items) != len(test.items) { t.Fatalf("(%s) unexpected length: %d vs %d", test.name, len(list.Items), len(test.items)) } // The order of elements in LIST is not guaranteed. mapping := make(map[string]int) for ix := range test.items { mapping[test.items[ix].Name] = ix } for ix := range list.Items { // Copy things that are set dynamically on the server expectedObj := test.items[mapping[list.Items[ix].Name]] expectedObj.SelfLink = list.Items[ix].SelfLink expectedObj.ResourceVersion = list.Items[ix].ResourceVersion expectedObj.Namespace = list.Items[ix].Namespace expectedObj.UID = list.Items[ix].UID expectedObj.CreationTimestamp = list.Items[ix].CreationTimestamp // We endure the order of items by sorting them (using 'mapping') // so that this function passes. if !reflect.DeepEqual(list.Items[ix], expectedObj) { t.Errorf("(%s) expected:\n%#v\nsaw:\n%#v\n", test.name, expectedObj, list.Items[ix]) } } }() } }
func testInstallThirdPartyResourceRemove(t *testing.T, version string) { master, etcdserver, server, assert := initThirdParty(t, version, "foo.company.com") defer server.Close() defer etcdserver.Terminate(t) expectedObj := Foo{ ObjectMeta: api.ObjectMeta{ Name: "test", }, TypeMeta: unversioned.TypeMeta{ Kind: "Foo", }, SomeField: "test field", OtherField: 10, } s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) defer destroyFunc() if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/test", "test", expectedObj)) { t.FailNow() return } secondObj := expectedObj secondObj.Name = "bar" if !assert.NoError(createThirdPartyObject(s, "/ThirdPartyResourceData/company.com/foos/default/bar", "bar", secondObj)) { t.FailNow() return } resp, err := http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { t.FailNow() return } if resp.StatusCode != http.StatusOK { t.Errorf("unexpected status: %v", resp) } item := Foo{} if err := decodeResponse(resp, &item); err != nil { t.Errorf("unexpected error: %v", err) } // TODO: validate etcd set things here item.ObjectMeta = expectedObj.ObjectMeta if !assert.True(reflect.DeepEqual(item, expectedObj)) { t.Errorf("expected:\n%v\nsaw:\n%v\n", expectedObj, item) } path := extensionsrest.MakeThirdPartyPath("company.com") master.RemoveThirdPartyResource(path + "/foos") resp, err = http.Get(server.URL + "/apis/company.com/" + version + "/namespaces/default/foos/test") if !assert.NoError(err) { return } if resp.StatusCode != http.StatusNotFound { t.Errorf("unexpected status: %v", resp) } expectedDeletedKeys := []string{ etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/test"), etcdtest.AddPrefix("/ThirdPartyResourceData/company.com/foos/default/bar"), } for _, key := range expectedDeletedKeys { thirdPartyObj := extensions.ThirdPartyResourceData{} s, destroyFunc := generic.NewRawStorage(master.thirdPartyStorageConfig) err := s.Get(context.TODO(), key, &thirdPartyObj, false) if !storage.IsNotFound(err) { t.Errorf("expected deletion didn't happen: %v", err) } destroyFunc() } installed := master.ListThirdPartyResources() if len(installed) != 0 { t.Errorf("Resource(s) still installed: %v", installed) } services := master.HandlerContainer.RegisteredWebServices() for ix := range services { if strings.HasPrefix(services[ix].RootPath(), "/apis/company.com") { t.Errorf("Web service still installed at %s: %#v", services[ix].RootPath(), services[ix]) } } }
func newStorage(t *testing.T) (*ScaleREST, *etcdtesting.EtcdTestServer, storage.Interface) { etcdStorage, server := registrytest.NewEtcdStorage(t, "") restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "controllers"} return NewStorage(restOptions).Scale, server, generic.NewRawStorage(etcdStorage) }