diff --git a/pkg/api/rest/create.go b/pkg/api/rest/create.go index c74c9a60d5..fafaad5169 100644 --- a/pkg/api/rest/create.go +++ b/pkg/api/rest/create.go @@ -87,12 +87,12 @@ func CheckGeneratedNameError(strategy RESTCreateStrategy, err error, obj runtime } // objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error. -func objectMetaAndKind(strategy RESTCreateStrategy, obj runtime.Object) (*api.ObjectMeta, string, error) { +func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, string, error) { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return nil, "", errors.NewInternalError(err) } - _, kind, err := strategy.ObjectVersionAndKind(obj) + _, kind, err := typer.ObjectVersionAndKind(obj) if err != nil { return nil, "", errors.NewInternalError(err) } diff --git a/pkg/api/rest/types.go b/pkg/api/rest/types.go index 155aca37da..71c710f37c 100644 --- a/pkg/api/rest/types.go +++ b/pkg/api/rest/types.go @@ -23,6 +23,27 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) +// ObjectFunc is a function to act on a given object. An error may be returned +// if the hook cannot be completed. An ObjectFunc may transform the provided +// object. +type ObjectFunc func(obj runtime.Object) error + +// AllFuncs returns an ObjectFunc that attempts to run all of the provided functions +// in order, returning early if there are any errors. +func AllFuncs(fns ...ObjectFunc) ObjectFunc { + return func(obj runtime.Object) error { + for _, fn := range fns { + if fn == nil { + continue + } + if err := fn(obj); err != nil { + return err + } + } + return nil + } +} + // rcStrategy implements behavior for Replication Controllers. // TODO: move to a replicationcontroller specific package. type rcStrategy struct { @@ -60,7 +81,7 @@ type podStrategy struct { // Pods is the default logic that applies when creating and updating Pod // objects. -var Pods RESTCreateStrategy = podStrategy{api.Scheme, api.SimpleNameGenerator} +var Pods = podStrategy{api.Scheme, api.SimpleNameGenerator} // NamespaceScoped is true for pods. func (podStrategy) NamespaceScoped() bool { @@ -70,7 +91,9 @@ func (podStrategy) NamespaceScoped() bool { // ResetBeforeCreate clears fields that are not allowed to be set by end users on creation. func (podStrategy) ResetBeforeCreate(obj runtime.Object) { pod := obj.(*api.Pod) - pod.Status = api.PodStatus{} + pod.Status = api.PodStatus{ + Phase: api.PodPending, + } } // Validate validates a new pod. @@ -79,6 +102,16 @@ func (podStrategy) Validate(obj runtime.Object) errors.ValidationErrorList { return validation.ValidatePod(pod) } +// AllowCreateOnUpdate is false for pods. +func (podStrategy) AllowCreateOnUpdate() bool { + return false +} + +// ValidateUpdate is the default update validation for an end user. +func (podStrategy) ValidateUpdate(obj, old runtime.Object) errors.ValidationErrorList { + return validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod)) +} + // svcStrategy implements behavior for Services // TODO: move to a service specific package. type svcStrategy struct { diff --git a/pkg/api/rest/update.go b/pkg/api/rest/update.go new file mode 100644 index 0000000000..b9eb43aaae --- /dev/null +++ b/pkg/api/rest/update.go @@ -0,0 +1,59 @@ +/* +Copyright 2014 Google Inc. 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 rest + +import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" +) + +// RESTUpdateStrategy defines the minimum validation, accepted input, and +// name generation behavior to update an object that follows Kubernetes +// API conventions. A resource may have many UpdateStrategies, depending on +// the call pattern in use. +type RESTUpdateStrategy interface { + runtime.ObjectTyper + // NamespaceScoped returns true if the object must be within a namespace. + NamespaceScoped() bool + // AllowCreateOnUpdate returns true if the object can be created by a PUT. + AllowCreateOnUpdate() bool + // ValidateUpdate is invoked after default fields in the object have been filled in before + // the object is persisted. + ValidateUpdate(obj, old runtime.Object) errors.ValidationErrorList +} + +// BeforeUpdate ensures that common operations for all resources are performed on update. It only returns +// errors that can be converted to api.Status. It will invoke update validation with the provided existing +// and updated objects. +func BeforeUpdate(strategy RESTUpdateStrategy, ctx api.Context, obj, old runtime.Object) error { + objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) + if kerr != nil { + return kerr + } + if strategy.NamespaceScoped() { + if !api.ValidNamespace(ctx, objectMeta) { + return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") + } + } else { + objectMeta.Namespace = api.NamespaceNone + } + if errs := strategy.ValidateUpdate(obj, old); len(errs) > 0 { + return errors.NewInvalid(kind, objectMeta.Name, errs) + } + return nil +} diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 9eaa9b4aa0..4f916bba76 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -561,7 +561,8 @@ func ValidatePodSpec(spec *api.PodSpec) errs.ValidationErrorList { return allErrs } -// ValidatePodUpdate tests to see if the update is legal +// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields +// that cannot be changed. func ValidatePodUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} @@ -584,6 +585,7 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList { allErrs = append(allErrs, errs.NewFieldInvalid("spec.containers", newPod.Spec.Containers, "some fields are immutable")) } + newPod.Status = oldPod.Status return allErrs }