From 5ea99310f1151c36243be3f12d463b8606bfc2ff Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Tue, 11 Aug 2015 09:05:40 +0200 Subject: [PATCH] Unify limitrange registry to be generic. --- api/swagger-spec/v1.json | 8 + pkg/master/master.go | 6 +- pkg/registry/limitrange/etcd/etcd.go | 59 ++++++++ .../{registry_test.go => etcd/etcd_test.go} | 27 ++-- pkg/registry/limitrange/registry.go | 49 ------ pkg/registry/limitrange/rest.go | 140 ++++-------------- pkg/registry/limitrange/rest_test.go | 17 --- 7 files changed, 120 insertions(+), 186 deletions(-) create mode 100644 pkg/registry/limitrange/etcd/etcd.go rename pkg/registry/limitrange/{registry_test.go => etcd/etcd_test.go} (78%) delete mode 100644 pkg/registry/limitrange/registry.go delete mode 100644 pkg/registry/limitrange/rest_test.go diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index 556a4b7936..c3f99f646c 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -1854,6 +1854,14 @@ "required": false, "allowMultiple": false }, + { + "type": "v1.DeleteOptions", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, { "type": "string", "paramType": "path", diff --git a/pkg/master/master.go b/pkg/master/master.go index d94071fb8d..e176b81d40 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -55,7 +55,7 @@ import ( endpointsetcd "k8s.io/kubernetes/pkg/registry/endpoint/etcd" "k8s.io/kubernetes/pkg/registry/event" expcontrolleretcd "k8s.io/kubernetes/pkg/registry/experimental/controller/etcd" - "k8s.io/kubernetes/pkg/registry/limitrange" + limitrangeetcd "k8s.io/kubernetes/pkg/registry/limitrange/etcd" "k8s.io/kubernetes/pkg/registry/minion" nodeetcd "k8s.io/kubernetes/pkg/registry/minion/etcd" "k8s.io/kubernetes/pkg/registry/namespace" @@ -433,7 +433,7 @@ func (m *Master) init(c *Config) { podTemplateStorage := podtemplateetcd.NewREST(c.DatabaseStorage) eventRegistry := event.NewEtcdRegistry(c.DatabaseStorage, uint64(c.EventTTL.Seconds())) - limitRangeRegistry := limitrange.NewEtcdRegistry(c.DatabaseStorage) + limitRangeStorage := limitrangeetcd.NewStorage(c.DatabaseStorage) resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.DatabaseStorage) secretStorage := secretetcd.NewStorage(c.DatabaseStorage) @@ -494,7 +494,7 @@ func (m *Master) init(c *Config) { "nodes/status": nodeStatusStorage, "events": event.NewStorage(eventRegistry), - "limitRanges": limitrange.NewStorage(limitRangeRegistry), + "limitRanges": limitRangeStorage, "resourceQuotas": resourceQuotaStorage, "resourceQuotas/status": resourceQuotaStatusStorage, "namespaces": namespaceStorage, diff --git a/pkg/registry/limitrange/etcd/etcd.go b/pkg/registry/limitrange/etcd/etcd.go new file mode 100644 index 0000000000..596376a548 --- /dev/null +++ b/pkg/registry/limitrange/etcd/etcd.go @@ -0,0 +1,59 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package etcd + +import ( + "k8s.io/kubernetes/pkg/api" + "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/registry/limitrange" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/storage" +) + +type REST struct { + *etcdgeneric.Etcd +} + +func NewStorage(s storage.Interface) *REST { + prefix := "/limitranges" + store := &etcdgeneric.Etcd{ + NewFunc: func() runtime.Object { return &api.LimitRange{} }, + NewListFunc: func() runtime.Object { return &api.LimitRangeList{} }, + 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.LimitRange).Name, nil + }, + PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { + return limitrange.MatchLimitRange(label, field) + }, + EndpointName: "limitranges", + + CreateStrategy: limitrange.Strategy, + UpdateStrategy: limitrange.Strategy, + + Storage: s, + } + return &REST{store} +} diff --git a/pkg/registry/limitrange/registry_test.go b/pkg/registry/limitrange/etcd/etcd_test.go similarity index 78% rename from pkg/registry/limitrange/registry_test.go rename to pkg/registry/limitrange/etcd/etcd_test.go index 4933b065a7..932a3dc945 100644 --- a/pkg/registry/limitrange/registry_test.go +++ b/pkg/registry/limitrange/etcd/etcd_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package limitrange +package etcd import ( "reflect" @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/registry/generic" etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd" "k8s.io/kubernetes/pkg/runtime" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" @@ -35,18 +34,18 @@ import ( "github.com/coreos/go-etcd/etcd" ) -func NewTestLimitRangeEtcdRegistry(t *testing.T) (*tools.FakeEtcdClient, generic.Registry) { +func NewTestLimitRangeStorage(t *testing.T) (*tools.FakeEtcdClient, *REST) { f := tools.NewFakeEtcdClient(t) f.TestIndex = true s := etcdstorage.NewEtcdStorage(f, testapi.Codec(), etcdtest.PathPrefix()) - return f, NewEtcdRegistry(s) + return f, NewStorage(s) } func TestLimitRangeCreate(t *testing.T) { limitRange := &api.LimitRange{ ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", + Name: "foo", + Namespace: "default", }, Spec: api.LimitRangeSpec{ Limits: []api.LimitRangeItem{ @@ -111,14 +110,24 @@ func TestLimitRangeCreate(t *testing.T) { } for name, item := range table { - fakeClient, registry := NewTestLimitRangeEtcdRegistry(t) + fakeClient, storage := NewTestLimitRangeStorage(t) fakeClient.Data[path] = item.existing - err := registry.CreateWithName(ctx, key, item.toCreate) + _, err := storage.Create(ctx, 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) { + received := fakeClient.Data[path] + var limitRange api.LimitRange + if err := testapi.Codec().DecodeInto([]byte(received.R.Node.Value), &limitRange); err != nil { + t.Errorf("unexpected error: %v", err) + } + // Unset CreationTimestamp and UID which are set automatically by infrastructure. + limitRange.ObjectMeta.CreationTimestamp = util.Time{} + limitRange.ObjectMeta.UID = "" + received.R.Node.Value = runtime.EncodeOrDie(testapi.Codec(), &limitRange) + + if e, a := item.expect, received; !reflect.DeepEqual(e, a) { t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a)) } } diff --git a/pkg/registry/limitrange/registry.go b/pkg/registry/limitrange/registry.go deleted file mode 100644 index 84e24c3475..0000000000 --- a/pkg/registry/limitrange/registry.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package limitrange - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/registry/generic" - etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/storage" -) - -// registry implements custom changes to generic.Etcd. -type registry struct { - *etcdgeneric.Etcd -} - -// NewEtcdRegistry returns a registry which will store LimitRange in the given storage -func NewEtcdRegistry(s storage.Interface) 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) - }, - Storage: s, - }, - } -} diff --git a/pkg/registry/limitrange/rest.go b/pkg/registry/limitrange/rest.go index f5a5926431..a2083744b4 100644 --- a/pkg/registry/limitrange/rest.go +++ b/pkg/registry/limitrange/rest.go @@ -17,137 +17,61 @@ limitations under the License. package limitrange import ( - "fmt" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/watch" + "k8s.io/kubernetes/pkg/util/fielderrors" ) -// REST provides the RESTStorage access patterns to work with LimitRange objects. -type REST struct { - registry generic.Registry +type limitrangeStrategy struct { + runtime.ObjectTyper + api.NameGenerator } -// NewStorage returns a new REST. You must use a registry created by -// NewEtcdRegistry unless you're testing. -func NewStorage(registry generic.Registry) *REST { - return &REST{ - registry: registry, - } +// Strategy is the default logic that applies when creating and updating +// LimitRange objects via the REST API. +var Strategy = limitrangeStrategy{api.Scheme, api.SimpleNameGenerator} + +func (limitrangeStrategy) NamespaceScoped() bool { + return true } -// Create a LimitRange object -func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { - limitRange, ok := obj.(*api.LimitRange) - if !ok { - return nil, fmt.Errorf("invalid object type") - } - - if !api.ValidNamespace(ctx, &limitRange.ObjectMeta) { - return nil, errors.NewConflict("limitRange", limitRange.Namespace, fmt.Errorf("LimitRange.Namespace does not match the provided context")) - } - +func (limitrangeStrategy) PrepareForCreate(obj runtime.Object) { + limitRange := obj.(*api.LimitRange) if len(limitRange.Name) == 0 { limitRange.Name = string(util.NewUUID()) } - - if errs := validation.ValidateLimitRange(limitRange); len(errs) > 0 { - return nil, errors.NewInvalid("limitRange", limitRange.Name, errs) - } - api.FillObjectMetaSystemFields(ctx, &limitRange.ObjectMeta) - - err := rs.registry.CreateWithName(ctx, limitRange.Name, limitRange) - if err != nil { - return nil, err - } - return rs.registry.Get(ctx, limitRange.Name) } -// Update updates a LimitRange object. -func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { - limitRange, ok := obj.(*api.LimitRange) - if !ok { - return nil, false, fmt.Errorf("invalid object type") - } - - if !api.ValidNamespace(ctx, &limitRange.ObjectMeta) { - return nil, false, errors.NewConflict("limitRange", limitRange.Namespace, fmt.Errorf("LimitRange.Namespace does not match the provided context")) - } - - oldObj, err := rs.registry.Get(ctx, limitRange.Name) - if err != nil { - return nil, false, err - } - - editLimitRange := oldObj.(*api.LimitRange) - - // set the editable fields on the existing object - editLimitRange.Labels = limitRange.Labels - editLimitRange.ResourceVersion = limitRange.ResourceVersion - editLimitRange.Annotations = limitRange.Annotations - editLimitRange.Spec = limitRange.Spec - - if errs := validation.ValidateLimitRange(editLimitRange); len(errs) > 0 { - return nil, false, errors.NewInvalid("limitRange", editLimitRange.Name, errs) - } - - if err := rs.registry.UpdateWithName(ctx, editLimitRange.Name, editLimitRange); err != nil { - return nil, false, err - } - out, err := rs.registry.Get(ctx, editLimitRange.Name) - return out, false, err +func (limitrangeStrategy) PrepareForUpdate(obj, old runtime.Object) { } -// Delete deletes the LimitRange with the specified name -func (rs *REST) Delete(ctx api.Context, name string) (runtime.Object, error) { - obj, err := rs.registry.Get(ctx, name) - if err != nil { - return nil, err - } - _, ok := obj.(*api.LimitRange) - if !ok { - return nil, fmt.Errorf("invalid object type") - } - return rs.registry.Delete(ctx, name, nil) +func (limitrangeStrategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList { + limitRange := obj.(*api.LimitRange) + return validation.ValidateLimitRange(limitRange) } -// Get gets a LimitRange with the specified name -func (rs *REST) Get(ctx api.Context, name string) (runtime.Object, error) { - obj, err := rs.registry.Get(ctx, name) - if err != nil { - return nil, err - } - limitRange, ok := obj.(*api.LimitRange) - if !ok { - return nil, fmt.Errorf("invalid object type") - } - return limitRange, err +func (limitrangeStrategy) AllowCreateOnUpdate() bool { + return true } -func (rs *REST) getAttrs(obj runtime.Object) (objLabels labels.Set, objFields fields.Set, err error) { +func (limitrangeStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList { + limitRange := obj.(*api.LimitRange) + return validation.ValidateLimitRange(limitRange) +} + +func (limitrangeStrategy) AllowUnconditionalUpdate() bool { + return true +} + +func MatchLimitRange(label labels.Selector, field fields.Selector) generic.Matcher { + return &generic.SelectionPredicate{label, field, getAttrs} +} + +func getAttrs(obj runtime.Object) (objLabels labels.Set, objFields fields.Set, err error) { return labels.Set{}, fields.Set{}, nil } - -func (rs *REST) List(ctx api.Context, label labels.Selector, field fields.Selector) (runtime.Object, error) { - return rs.registry.ListPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) -} - -func (rs *REST) Watch(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) { - return rs.registry.WatchPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) -} - -// New returns a new api.LimitRange -func (*REST) New() runtime.Object { - return &api.LimitRange{} -} - -func (*REST) NewList() runtime.Object { - return &api.LimitRangeList{} -} diff --git a/pkg/registry/limitrange/rest_test.go b/pkg/registry/limitrange/rest_test.go deleted file mode 100644 index aadeb31330..0000000000 --- a/pkg/registry/limitrange/rest_test.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package limitrange