1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package rest
18
19import (
20	"context"
21	"fmt"
22
23	"k8s.io/apimachinery/pkg/api/errors"
24	"k8s.io/apimachinery/pkg/api/meta"
25	genericvalidation "k8s.io/apimachinery/pkg/api/validation"
26	"k8s.io/apimachinery/pkg/api/validation/path"
27	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28	"k8s.io/apimachinery/pkg/runtime"
29	"k8s.io/apimachinery/pkg/util/validation/field"
30	"k8s.io/apiserver/pkg/admission"
31	"k8s.io/apiserver/pkg/features"
32	utilfeature "k8s.io/apiserver/pkg/util/feature"
33)
34
35// RESTUpdateStrategy defines the minimum validation, accepted input, and
36// name generation behavior to update an object that follows Kubernetes
37// API conventions. A resource may have many UpdateStrategies, depending on
38// the call pattern in use.
39type RESTUpdateStrategy interface {
40	runtime.ObjectTyper
41	// NamespaceScoped returns true if the object must be within a namespace.
42	NamespaceScoped() bool
43	// AllowCreateOnUpdate returns true if the object can be created by a PUT.
44	AllowCreateOnUpdate() bool
45	// PrepareForUpdate is invoked on update before validation to normalize
46	// the object.  For example: remove fields that are not to be persisted,
47	// sort order-insensitive list fields, etc.  This should not remove fields
48	// whose presence would be considered a validation error.
49	PrepareForUpdate(ctx context.Context, obj, old runtime.Object)
50	// ValidateUpdate is invoked after default fields in the object have been
51	// filled in before the object is persisted.  This method should not mutate
52	// the object.
53	ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList
54	// Canonicalize allows an object to be mutated into a canonical form. This
55	// ensures that code that operates on these objects can rely on the common
56	// form for things like comparison.  Canonicalize is invoked after
57	// validation has succeeded but before the object has been persisted.
58	// This method may mutate the object.
59	Canonicalize(obj runtime.Object)
60	// AllowUnconditionalUpdate returns true if the object can be updated
61	// unconditionally (irrespective of the latest resource version), when
62	// there is no resource version specified in the object.
63	AllowUnconditionalUpdate() bool
64}
65
66// TODO: add other common fields that require global validation.
67func validateCommonFields(obj, old runtime.Object, strategy RESTUpdateStrategy) (field.ErrorList, error) {
68	allErrs := field.ErrorList{}
69	objectMeta, err := meta.Accessor(obj)
70	if err != nil {
71		return nil, fmt.Errorf("failed to get new object metadata: %v", err)
72	}
73	oldObjectMeta, err := meta.Accessor(old)
74	if err != nil {
75		return nil, fmt.Errorf("failed to get old object metadata: %v", err)
76	}
77	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata"))...)
78	allErrs = append(allErrs, genericvalidation.ValidateObjectMetaAccessorUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
79
80	return allErrs, nil
81}
82
83// BeforeUpdate ensures that common operations for all resources are performed on update. It only returns
84// errors that can be converted to api.Status. It will invoke update validation with the provided existing
85// and updated objects.
86// It sets zero values only if the object does not have a zero value for the respective field.
87func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old runtime.Object) error {
88	objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
89	if kerr != nil {
90		return kerr
91	}
92	if strategy.NamespaceScoped() {
93		if !ValidNamespace(ctx, objectMeta) {
94			return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
95		}
96	} else if len(objectMeta.GetNamespace()) > 0 {
97		objectMeta.SetNamespace(metav1.NamespaceNone)
98	}
99
100	// Ensure requests cannot update generation
101	oldMeta, err := meta.Accessor(old)
102	if err != nil {
103		return err
104	}
105	objectMeta.SetGeneration(oldMeta.GetGeneration())
106
107	// Ensure managedFields state is removed unless ServerSideApply is enabled
108	if !utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
109		oldMeta.SetManagedFields(nil)
110		objectMeta.SetManagedFields(nil)
111	}
112
113	strategy.PrepareForUpdate(ctx, obj, old)
114
115	// ClusterName is ignored and should not be saved
116	if len(objectMeta.GetClusterName()) > 0 {
117		objectMeta.SetClusterName("")
118	}
119	// Use the existing UID if none is provided
120	if len(objectMeta.GetUID()) == 0 {
121		objectMeta.SetUID(oldMeta.GetUID())
122	}
123	// ignore changes to timestamp
124	if oldCreationTime := oldMeta.GetCreationTimestamp(); !oldCreationTime.IsZero() {
125		objectMeta.SetCreationTimestamp(oldMeta.GetCreationTimestamp())
126	}
127	// an update can never remove/change a deletion timestamp
128	if !oldMeta.GetDeletionTimestamp().IsZero() {
129		objectMeta.SetDeletionTimestamp(oldMeta.GetDeletionTimestamp())
130	}
131	// an update can never remove/change grace period seconds
132	if oldMeta.GetDeletionGracePeriodSeconds() != nil && objectMeta.GetDeletionGracePeriodSeconds() == nil {
133		objectMeta.SetDeletionGracePeriodSeconds(oldMeta.GetDeletionGracePeriodSeconds())
134	}
135
136	// Ensure some common fields, like UID, are validated for all resources.
137	errs, err := validateCommonFields(obj, old, strategy)
138	if err != nil {
139		return errors.NewInternalError(err)
140	}
141
142	errs = append(errs, strategy.ValidateUpdate(ctx, obj, old)...)
143	if len(errs) > 0 {
144		return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs)
145	}
146
147	strategy.Canonicalize(obj)
148
149	return nil
150}
151
152// TransformFunc is a function to transform and return newObj
153type TransformFunc func(ctx context.Context, newObj runtime.Object, oldObj runtime.Object) (transformedNewObj runtime.Object, err error)
154
155// defaultUpdatedObjectInfo implements UpdatedObjectInfo
156type defaultUpdatedObjectInfo struct {
157	// obj is the updated object
158	obj runtime.Object
159
160	// transformers is an optional list of transforming functions that modify or
161	// replace obj using information from the context, old object, or other sources.
162	transformers []TransformFunc
163}
164
165// DefaultUpdatedObjectInfo returns an UpdatedObjectInfo impl based on the specified object.
166func DefaultUpdatedObjectInfo(obj runtime.Object, transformers ...TransformFunc) UpdatedObjectInfo {
167	return &defaultUpdatedObjectInfo{obj, transformers}
168}
169
170// Preconditions satisfies the UpdatedObjectInfo interface.
171func (i *defaultUpdatedObjectInfo) Preconditions() *metav1.Preconditions {
172	// Attempt to get the UID out of the object
173	accessor, err := meta.Accessor(i.obj)
174	if err != nil {
175		// If no UID can be read, no preconditions are possible
176		return nil
177	}
178
179	// If empty, no preconditions needed
180	uid := accessor.GetUID()
181	if len(uid) == 0 {
182		return nil
183	}
184
185	return &metav1.Preconditions{UID: &uid}
186}
187
188// UpdatedObject satisfies the UpdatedObjectInfo interface.
189// It returns a copy of the held obj, passed through any configured transformers.
190func (i *defaultUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runtime.Object) (runtime.Object, error) {
191	var err error
192	// Start with the configured object
193	newObj := i.obj
194
195	// If the original is non-nil (might be nil if the first transformer builds the object from the oldObj), make a copy,
196	// so we don't return the original. BeforeUpdate can mutate the returned object, doing things like clearing ResourceVersion.
197	// If we're re-called, we need to be able to return the pristine version.
198	if newObj != nil {
199		newObj = newObj.DeepCopyObject()
200	}
201
202	// Allow any configured transformers to update the new object
203	for _, transformer := range i.transformers {
204		newObj, err = transformer(ctx, newObj, oldObj)
205		if err != nil {
206			return nil, err
207		}
208	}
209
210	return newObj, nil
211}
212
213// wrappedUpdatedObjectInfo allows wrapping an existing objInfo and
214// chaining additional transformations/checks on the result of UpdatedObject()
215type wrappedUpdatedObjectInfo struct {
216	// obj is the updated object
217	objInfo UpdatedObjectInfo
218
219	// transformers is an optional list of transforming functions that modify or
220	// replace obj using information from the context, old object, or other sources.
221	transformers []TransformFunc
222}
223
224// WrapUpdatedObjectInfo returns an UpdatedObjectInfo impl that delegates to
225// the specified objInfo, then calls the passed transformers
226func WrapUpdatedObjectInfo(objInfo UpdatedObjectInfo, transformers ...TransformFunc) UpdatedObjectInfo {
227	return &wrappedUpdatedObjectInfo{objInfo, transformers}
228}
229
230// Preconditions satisfies the UpdatedObjectInfo interface.
231func (i *wrappedUpdatedObjectInfo) Preconditions() *metav1.Preconditions {
232	return i.objInfo.Preconditions()
233}
234
235// UpdatedObject satisfies the UpdatedObjectInfo interface.
236// It delegates to the wrapped objInfo and passes the result through any configured transformers.
237func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runtime.Object) (runtime.Object, error) {
238	newObj, err := i.objInfo.UpdatedObject(ctx, oldObj)
239	if err != nil {
240		return newObj, err
241	}
242
243	// Allow any configured transformers to update the new object or error
244	for _, transformer := range i.transformers {
245		newObj, err = transformer(ctx, newObj, oldObj)
246		if err != nil {
247			return nil, err
248		}
249	}
250
251	return newObj, nil
252}
253
254// AdmissionToValidateObjectUpdateFunc converts validating admission to a rest validate object update func
255func AdmissionToValidateObjectUpdateFunc(admit admission.Interface, staticAttributes admission.Attributes, o admission.ObjectInterfaces) ValidateObjectUpdateFunc {
256	validatingAdmission, ok := admit.(admission.ValidationInterface)
257	if !ok {
258		return func(ctx context.Context, obj, old runtime.Object) error { return nil }
259	}
260	return func(ctx context.Context, obj, old runtime.Object) error {
261		finalAttributes := admission.NewAttributesRecord(
262			obj,
263			old,
264			staticAttributes.GetKind(),
265			staticAttributes.GetNamespace(),
266			staticAttributes.GetName(),
267			staticAttributes.GetResource(),
268			staticAttributes.GetSubresource(),
269			staticAttributes.GetOperation(),
270			staticAttributes.GetOperationOptions(),
271			staticAttributes.IsDryRun(),
272			staticAttributes.GetUserInfo(),
273		)
274		if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
275			return nil
276		}
277		return validatingAdmission.Validate(ctx, finalAttributes, o)
278	}
279}
280