diff --git a/pkg/api/meta.go b/pkg/api/meta.go index 8beb0672d52..7ad2e1d3da8 100644 --- a/pkg/api/meta.go +++ b/pkg/api/meta.go @@ -22,23 +22,8 @@ import ( "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request" - "k8s.io/kubernetes/pkg/util/uuid" ) -// FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta. -func FillObjectMetaSystemFields(ctx genericapirequest.Context, meta *ObjectMeta) { - meta.CreationTimestamp = metav1.Now() - // allows admission controllers to assign a UID earlier in the request processing - // to support tracking resources pending creation. - uid, found := genericapirequest.UIDFrom(ctx) - if !found { - uid = uuid.NewUUID() - } - meta.UID = uid - meta.SelfLink = "" -} - // HasObjectMetaSystemFieldValues returns true if fields that are managed by the system on ObjectMeta have values. func HasObjectMetaSystemFieldValues(meta *ObjectMeta) bool { return !meta.CreationTimestamp.Time.IsZero() || diff --git a/pkg/api/meta_test.go b/pkg/api/meta_test.go index 4c44e6082a3..e9d534781fd 100644 --- a/pkg/api/meta_test.go +++ b/pkg/api/meta_test.go @@ -28,45 +28,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/kubernetes/pkg/api" - genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request" - "k8s.io/kubernetes/pkg/util/uuid" ) var _ meta.Object = &api.ObjectMeta{} -// TestFillObjectMetaSystemFields validates that system populated fields are set on an object -func TestFillObjectMetaSystemFields(t *testing.T) { - ctx := genericapirequest.NewDefaultContext() - resource := api.ObjectMeta{} - api.FillObjectMetaSystemFields(ctx, &resource) - if resource.CreationTimestamp.Time.IsZero() { - t.Errorf("resource.CreationTimestamp is zero") - } else if len(resource.UID) == 0 { - t.Errorf("resource.UID missing") - } - // verify we can inject a UID - uid := uuid.NewUUID() - ctx = genericapirequest.WithUID(ctx, uid) - resource = api.ObjectMeta{} - api.FillObjectMetaSystemFields(ctx, &resource) - if resource.UID != uid { - t.Errorf("resource.UID expected: %v, actual: %v", uid, resource.UID) - } -} - -// TestHasObjectMetaSystemFieldValues validates that true is returned if and only if all fields are populated -func TestHasObjectMetaSystemFieldValues(t *testing.T) { - ctx := genericapirequest.NewDefaultContext() - resource := api.ObjectMeta{} - if api.HasObjectMetaSystemFieldValues(&resource) { - t.Errorf("the resource does not have all fields yet populated, but incorrectly reports it does") - } - api.FillObjectMetaSystemFields(ctx, &resource) - if !api.HasObjectMetaSystemFieldValues(&resource) { - t.Errorf("the resource does have all fields populated, but incorrectly reports it does not") - } -} - func getObjectMetaAndOwnerReferences() (objectMeta api.ObjectMeta, metaOwnerReferences []metav1.OwnerReference) { fuzz.New().NilChance(.5).NumElements(1, 5).Fuzz(&objectMeta) references := objectMeta.OwnerReferences diff --git a/pkg/api/resource_helpers.go b/pkg/api/resource_helpers.go index e3eb31888eb..9ca3297e66b 100644 --- a/pkg/api/resource_helpers.go +++ b/pkg/api/resource_helpers.go @@ -21,7 +21,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/api/resource" - genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request" ) // Returns string version of ResourceName. @@ -228,13 +227,3 @@ func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, li } return } - -// ValidNamespace returns false if the namespace on the context differs from the resource. If the resource has no namespace, it is set to the value in the context. -// TODO(sttts): move into pkg/genericapiserver/api -func ValidNamespace(ctx genericapirequest.Context, resource *ObjectMeta) bool { - ns, ok := genericapirequest.NamespaceFrom(ctx) - if len(resource.Namespace) == 0 { - resource.Namespace = ns - } - return ns == resource.Namespace && ok -} diff --git a/pkg/api/rest/create.go b/pkg/api/rest/create.go index 9b523241447..b510015fde9 100644 --- a/pkg/api/rest/create.go +++ b/pkg/api/rest/create.go @@ -23,7 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/validation/genericvalidation" - path "k8s.io/kubernetes/pkg/api/validation/path" + "k8s.io/kubernetes/pkg/api/validation/path" genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request" ) @@ -61,7 +61,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx genericapirequest.Context, ob } if strategy.NamespaceScoped() { - if !api.ValidNamespace(ctx, objectMeta) { + if !ValidNamespace(ctx, objectMeta) { return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") } } else { @@ -70,7 +70,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx genericapirequest.Context, ob objectMeta.DeletionTimestamp = nil objectMeta.DeletionGracePeriodSeconds = nil strategy.PrepareForCreate(ctx, obj) - api.FillObjectMetaSystemFields(ctx, objectMeta) + FillObjectMetaSystemFields(ctx, objectMeta) api.GenerateName(strategy, objectMeta) // ClusterName is ignored and should not be saved diff --git a/pkg/api/rest/meta.go b/pkg/api/rest/meta.go new file mode 100644 index 00000000000..155f8882107 --- /dev/null +++ b/pkg/api/rest/meta.go @@ -0,0 +1,47 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 rest + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api" + genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request" + "k8s.io/kubernetes/pkg/util/uuid" +) + +// FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta. +func FillObjectMetaSystemFields(ctx genericapirequest.Context, meta *api.ObjectMeta) { + meta.CreationTimestamp = metav1.Now() + // allows admission controllers to assign a UID earlier in the request processing + // to support tracking resources pending creation. + uid, found := genericapirequest.UIDFrom(ctx) + if !found { + uid = uuid.NewUUID() + } + meta.UID = uid + meta.SelfLink = "" +} + +// ValidNamespace returns false if the namespace on the context differs from the resource. If the resource has no namespace, it is set to the value in the context. +// TODO(sttts): move into pkg/genericapiserver/api +func ValidNamespace(ctx genericapirequest.Context, resource *api.ObjectMeta) bool { + ns, ok := genericapirequest.NamespaceFrom(ctx) + if len(resource.Namespace) == 0 { + resource.Namespace = ns + } + return ns == resource.Namespace && ok +} diff --git a/pkg/api/rest/meta_test.go b/pkg/api/rest/meta_test.go new file mode 100644 index 00000000000..624ed4ba4f4 --- /dev/null +++ b/pkg/api/rest/meta_test.go @@ -0,0 +1,87 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 rest + +import ( + "testing" + + + "k8s.io/kubernetes/pkg/api" + genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request" + "k8s.io/kubernetes/pkg/util/uuid" +) + +// TestFillObjectMetaSystemFields validates that system populated fields are set on an object +func TestFillObjectMetaSystemFields(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + resource := api.ObjectMeta{} + FillObjectMetaSystemFields(ctx, &resource) + if resource.CreationTimestamp.Time.IsZero() { + t.Errorf("resource.CreationTimestamp is zero") + } else if len(resource.UID) == 0 { + t.Errorf("resource.UID missing") + } + // verify we can inject a UID + uid := uuid.NewUUID() + ctx = genericapirequest.WithUID(ctx, uid) + resource = api.ObjectMeta{} + FillObjectMetaSystemFields(ctx, &resource) + if resource.UID != uid { + t.Errorf("resource.UID expected: %v, actual: %v", uid, resource.UID) + } +} + +// TestHasObjectMetaSystemFieldValues validates that true is returned if and only if all fields are populated +func TestHasObjectMetaSystemFieldValues(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + resource := api.ObjectMeta{} + if api.HasObjectMetaSystemFieldValues(&resource) { + t.Errorf("the resource does not have all fields yet populated, but incorrectly reports it does") + } + FillObjectMetaSystemFields(ctx, &resource) + if !api.HasObjectMetaSystemFieldValues(&resource) { + t.Errorf("the resource does have all fields populated, but incorrectly reports it does not") + } +} + + +// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update +func TestValidNamespace(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + namespace, _ := genericapirequest.NamespaceFrom(ctx) + resource := api.ReplicationController{} + if !ValidNamespace(ctx, &resource.ObjectMeta) { + t.Fatalf("expected success") + } + if namespace != resource.Namespace { + t.Fatalf("expected resource to have the default namespace assigned during validation") + } + resource = api.ReplicationController{ObjectMeta: api.ObjectMeta{Namespace: "other"}} + if ValidNamespace(ctx, &resource.ObjectMeta) { + t.Fatalf("Expected error that resource and context errors do not match because resource has different namespace") + } + ctx = genericapirequest.NewContext() + if ValidNamespace(ctx, &resource.ObjectMeta) { + t.Fatalf("Expected error that resource and context errors do not match since context has no namespace") + } + + ctx = genericapirequest.NewContext() + ns := genericapirequest.NamespaceValue(ctx) + if ns != "" { + t.Fatalf("Expected the empty string") + } +} \ No newline at end of file diff --git a/pkg/api/rest/update.go b/pkg/api/rest/update.go index 4534888675a..ed0dc0fe2a0 100644 --- a/pkg/api/rest/update.go +++ b/pkg/api/rest/update.go @@ -81,7 +81,7 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx genericapirequest.Context, ob return kerr } if strategy.NamespaceScoped() { - if !api.ValidNamespace(ctx, objectMeta) { + if !ValidNamespace(ctx, objectMeta) { return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") } } else { diff --git a/pkg/genericapiserver/api/request/context_test.go b/pkg/genericapiserver/api/request/context_test.go index 4c5f766b6b7..5d2608f5c43 100644 --- a/pkg/genericapiserver/api/request/context_test.go +++ b/pkg/genericapiserver/api/request/context_test.go @@ -43,33 +43,6 @@ func TestNamespaceContext(t *testing.T) { } } -// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update -func TestValidNamespace(t *testing.T) { - ctx := genericapirequest.NewDefaultContext() - namespace, _ := genericapirequest.NamespaceFrom(ctx) - resource := api.ReplicationController{} - if !api.ValidNamespace(ctx, &resource.ObjectMeta) { - t.Fatalf("expected success") - } - if namespace != resource.Namespace { - t.Fatalf("expected resource to have the default namespace assigned during validation") - } - resource = api.ReplicationController{ObjectMeta: api.ObjectMeta{Namespace: "other"}} - if api.ValidNamespace(ctx, &resource.ObjectMeta) { - t.Fatalf("Expected error that resource and context errors do not match because resource has different namespace") - } - ctx = genericapirequest.NewContext() - if api.ValidNamespace(ctx, &resource.ObjectMeta) { - t.Fatalf("Expected error that resource and context errors do not match since context has no namespace") - } - - ctx = genericapirequest.NewContext() - ns := genericapirequest.NamespaceValue(ctx) - if ns != "" { - t.Fatalf("Expected the empty string") - } -} - //TestUserContext validates that a userinfo can be get/set on a context object func TestUserContext(t *testing.T) { ctx := genericapirequest.NewContext() diff --git a/pkg/registry/core/service/rest.go b/pkg/registry/core/service/rest.go index 798145636ad..f0d49478b34 100644 --- a/pkg/registry/core/service/rest.go +++ b/pkg/registry/core/service/rest.go @@ -381,7 +381,7 @@ func (rs *REST) Update(ctx genericapirequest.Context, name string, objInfo rest. } service := obj.(*api.Service) - if !api.ValidNamespace(ctx, &service.ObjectMeta) { + if !rest.ValidNamespace(ctx, &service.ObjectMeta) { return nil, false, errors.NewConflict(api.Resource("services"), service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) }