func TestEtcdDeleteService(t *testing.T) { ctx := api.NewDefaultContext() fakeClient := tools.NewFakeEtcdClient(t) registry := NewTestEtcdRegistryWithPods(fakeClient) key, _ := etcdgeneric.NamespaceKeyFunc(ctx, "/services/specs", "foo") key = etcdtest.AddPrefix(key) fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, &api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}), 0) path, _ := etcdgeneric.NamespaceKeyFunc(ctx, "/services/endpoints", "foo") endpointsKey := etcdtest.AddPrefix(path) fakeClient.Set(endpointsKey, runtime.EncodeOrDie(latest.Codec, &api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}}), 0) err := registry.DeleteService(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } if len(fakeClient.DeletedKeys) != 2 { t.Errorf("Expected 2 delete, found %#v", fakeClient.DeletedKeys) } if fakeClient.DeletedKeys[0] != key { t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key) } if fakeClient.DeletedKeys[1] != endpointsKey { t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[1], endpointsKey) } }
// NewStorage returns a RESTStorage object that will work against ResourceQuota objects. func NewStorage(h tools.EtcdHelper) (*REST, *StatusREST) { prefix := "/resourcequotas" store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.ResourceQuota{} }, NewListFunc: func() runtime.Object { return &api.ResourceQuotaList{} }, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.ResourceQuota).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return resourcequota.MatchResourceQuota(label, field) }, EndpointName: "resourcequotas", Helper: h, } store.CreateStrategy = resourcequota.Strategy store.UpdateStrategy = resourcequota.Strategy store.ReturnDeletedObject = true statusStore := *store statusStore.UpdateStrategy = resourcequota.StatusStrategy return &REST{store}, &StatusREST{store: &statusStore} }
// NewStorage returns a RESTStorage object that will work against endpoints. func NewStorage(h tools.EtcdHelper) *REST { prefix := "/services/endpoints" return &REST{ &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.Endpoints{} }, NewListFunc: func() runtime.Object { return &api.EndpointsList{} }, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Endpoints).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return endpoint.MatchEndpoints(label, field) }, EndpointName: "endpoints", CreateStrategy: endpoint.Strategy, UpdateStrategy: endpoint.Strategy, Helper: h, }, } }
// NewStorage returns a registry which will store Secret in the given helper func NewStorage(h tools.EtcdHelper) *REST { prefix := "/secrets" store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.Secret{} }, NewListFunc: func() runtime.Object { return &api.SecretList{} }, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Secret).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return secret.Matcher(label, field) }, EndpointName: "secrets", Helper: h, } store.CreateStrategy = secret.Strategy store.UpdateStrategy = secret.Strategy return &REST{store} }
// NewREST returns a RESTStorage object that will work against pod templates. func NewREST(h tools.EtcdHelper) *REST { prefix := "/podtemplates" store := etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.PodTemplate{} }, NewListFunc: func() runtime.Object { return &api.PodTemplateList{} }, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.PodTemplate).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return podtemplate.MatchPodTemplate(label, field) }, EndpointName: "podtemplates", CreateStrategy: podtemplate.Strategy, UpdateStrategy: podtemplate.Strategy, ReturnDeletedObject: true, Helper: h, } return &REST{store} }
// NewEtcdRegistry returns a registry which will store LimitRange in the given helper func NewEtcdRegistry(h tools.EtcdHelper) generic.Registry { prefix := "/limitranges" return registry{ Etcd: &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.LimitRange{} }, NewListFunc: func() runtime.Object { return &api.LimitRangeList{} }, EndpointName: "limitranges", KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, id) }, Helper: h, }, } }
// NewStorage returns a RESTStorage object that will work against pods. func NewStorage(h tools.EtcdHelper, k client.ConnectionInfoGetter) PodStorage { prefix := "/pods" store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.Pod{} }, NewListFunc: func() runtime.Object { return &api.PodList{} }, KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Pod).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return pod.MatchPod(label, field) }, EndpointName: "pods", Helper: h, } statusStore := *store bindings := &podLifecycle{} store.CreateStrategy = pod.Strategy store.UpdateStrategy = pod.Strategy store.AfterUpdate = bindings.AfterUpdate store.DeleteStrategy = pod.Strategy store.ReturnDeletedObject = true store.AfterDelete = bindings.AfterDelete statusStore.UpdateStrategy = pod.StatusStrategy return PodStorage{ Pod: &REST{*store}, Binding: &BindingREST{store: store}, Status: &StatusREST{store: &statusStore}, Log: &LogREST{store: store, qingletConn: k}, Proxy: &ProxyREST{store: store}, Exec: &ExecREST{store: store, qingletConn: k}, PortForward: &PortForwardREST{store: store, qingletConn: k}, } }
// NewEtcdRegistry returns a registry which will store Events in the given // EtcdHelper. ttl is the time that Events will be retained by the system. func NewEtcdRegistry(h tools.EtcdHelper, ttl uint64) generic.Registry { prefix := "/events" return registry{ Etcd: &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.Event{} }, NewListFunc: func() runtime.Object { return &api.EventList{} }, EndpointName: "events", KeyRootFunc: func(ctx api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, prefix, id) }, TTLFunc: func(runtime.Object, uint64, bool) (uint64, error) { return ttl, nil }, Helper: h, }, } }
// NewREST returns a RESTStorage object that will work against replication controllers. func NewREST(h tools.EtcdHelper) *REST { store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.ReplicationController{} }, // NewListFunc returns an object capable of storing results of an etcd list. NewListFunc: func() runtime.Object { return &api.ReplicationControllerList{} }, // 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 api.Context) string { return etcdgeneric.NamespaceKeyRootFunc(ctx, controllerPrefix) }, // Produces a path that etcd understands, to the resource by combining // the namespace in the context with the given prefix KeyFunc: func(ctx api.Context, name string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, controllerPrefix, name) }, // Retrieve the name field of a replication controller ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.ReplicationController).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 controller.MatchController(label, field) }, EndpointName: "replicationControllers", // Used to validate controller creation CreateStrategy: controller.Strategy, // Used to validate controller updates UpdateStrategy: controller.Strategy, Helper: h, } return &REST{store} }
func TestEventCreate(t *testing.T) { eventA := &api.Event{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault}, Reason: "forTesting", } eventB := &api.Event{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault}, Reason: "forTesting", } nodeWithEventA := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Codec(), eventA), ModifiedIndex: 1, CreatedIndex: 1, TTL: int64(testTTL), }, }, E: nil, } emptyNode := tools.EtcdResponseWithError{ R: &etcd.Response{}, E: tools.EtcdErrorNotFound, } ctx := api.NewDefaultContext() key := "foo" path, err := etcdgeneric.NamespaceKeyFunc(ctx, "/events", key) path = etcdtest.AddPrefix(path) if err != nil { t.Errorf("Unexpected error: %v", err) } table := map[string]struct { existing tools.EtcdResponseWithError expect tools.EtcdResponseWithError toCreate runtime.Object errOK func(error) bool }{ "normal": { existing: emptyNode, expect: nodeWithEventA, toCreate: eventA, errOK: func(err error) bool { return err == nil }, }, "preExisting": { existing: nodeWithEventA, expect: nodeWithEventA, toCreate: eventB, errOK: errors.IsAlreadyExists, }, } for name, item := range table { fakeClient, registry := NewTestEventEtcdRegistry(t) fakeClient.Data[path] = item.existing err := registry.CreateWithName(ctx, key, item.toCreate) if !item.errOK(err) { t.Errorf("%v: unexpected error: %v", name, err) } if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) { t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a)) } } }
func TestLimitRangeCreate(t *testing.T) { limitRange := &api.LimitRange{ ObjectMeta: api.ObjectMeta{ Name: "abc", Namespace: "foo", }, Spec: api.LimitRangeSpec{ Limits: []api.LimitRangeItem{ { Type: api.LimitTypePod, Max: api.ResourceList{ api.ResourceCPU: resource.MustParse("100"), api.ResourceMemory: resource.MustParse("10000"), }, Min: api.ResourceList{ api.ResourceCPU: resource.MustParse("0"), api.ResourceMemory: resource.MustParse("100"), }, }, }, }, } nodeWithLimitRange := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Codec(), limitRange), ModifiedIndex: 1, CreatedIndex: 1, }, }, E: nil, } emptyNode := tools.EtcdResponseWithError{ R: &etcd.Response{}, E: tools.EtcdErrorNotFound, } ctx := api.NewDefaultContext() key := "foo" prefix := etcdtest.AddPrefix("limitranges") path, err := etcdgeneric.NamespaceKeyFunc(ctx, prefix, key) if err != nil { t.Errorf("Unexpected error: %v", err) } table := map[string]struct { existing tools.EtcdResponseWithError expect tools.EtcdResponseWithError toCreate runtime.Object errOK func(error) bool }{ "normal": { existing: emptyNode, expect: nodeWithLimitRange, toCreate: limitRange, errOK: func(err error) bool { return err == nil }, }, "preExisting": { existing: nodeWithLimitRange, expect: nodeWithLimitRange, toCreate: limitRange, errOK: errors.IsAlreadyExists, }, } for name, item := range table { fakeClient, registry := NewTestLimitRangeEtcdRegistry(t) fakeClient.Data[path] = item.existing err := registry.CreateWithName(ctx, key, item.toCreate) if !item.errOK(err) { t.Errorf("%v: unexpected error: %v", name, err) } if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) { t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a)) } } }
// makeControllerKey constructs etcd paths to controller items enforcing namespace rules. func makeControllerKey(ctx api.Context, id string) (string, error) { return etcdgeneric.NamespaceKeyFunc(ctx, controllerPrefix, id) }