1/*
2Copyright 2015 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 storage
18
19import (
20	"context"
21	"fmt"
22
23	apierrors "k8s.io/apimachinery/pkg/api/errors"
24	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
25	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26	"k8s.io/apimachinery/pkg/runtime"
27	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
28	"k8s.io/apimachinery/pkg/watch"
29	"k8s.io/apiserver/pkg/registry/generic"
30	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
31	"k8s.io/apiserver/pkg/registry/rest"
32	"k8s.io/apiserver/pkg/storage"
33	storageerr "k8s.io/apiserver/pkg/storage/errors"
34	"k8s.io/apiserver/pkg/util/dryrun"
35	api "k8s.io/kubernetes/pkg/apis/core"
36	"k8s.io/kubernetes/pkg/printers"
37	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
38	printerstorage "k8s.io/kubernetes/pkg/printers/storage"
39	"k8s.io/kubernetes/pkg/registry/core/namespace"
40	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
41)
42
43// rest implements a RESTStorage for namespaces
44type REST struct {
45	store  *genericregistry.Store
46	status *genericregistry.Store
47}
48
49// StatusREST implements the REST endpoint for changing the status of a namespace.
50type StatusREST struct {
51	store *genericregistry.Store
52}
53
54// FinalizeREST implements the REST endpoint for finalizing a namespace.
55type FinalizeREST struct {
56	store *genericregistry.Store
57}
58
59// NewREST returns a RESTStorage object that will work against namespaces.
60func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *FinalizeREST, error) {
61	store := &genericregistry.Store{
62		NewFunc:                  func() runtime.Object { return &api.Namespace{} },
63		NewListFunc:              func() runtime.Object { return &api.NamespaceList{} },
64		PredicateFunc:            namespace.MatchNamespace,
65		DefaultQualifiedResource: api.Resource("namespaces"),
66
67		CreateStrategy:      namespace.Strategy,
68		UpdateStrategy:      namespace.Strategy,
69		DeleteStrategy:      namespace.Strategy,
70		ResetFieldsStrategy: namespace.Strategy,
71		ReturnDeletedObject: true,
72
73		ShouldDeleteDuringUpdate: ShouldDeleteNamespaceDuringUpdate,
74
75		TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
76	}
77	options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: namespace.GetAttrs}
78	if err := store.CompleteWithOptions(options); err != nil {
79		return nil, nil, nil, err
80	}
81
82	statusStore := *store
83	statusStore.UpdateStrategy = namespace.StatusStrategy
84	statusStore.ResetFieldsStrategy = namespace.StatusStrategy
85
86	finalizeStore := *store
87	finalizeStore.UpdateStrategy = namespace.FinalizeStrategy
88	finalizeStore.ResetFieldsStrategy = namespace.FinalizeStrategy
89
90	return &REST{store: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore}, nil
91}
92
93func (r *REST) NamespaceScoped() bool {
94	return r.store.NamespaceScoped()
95}
96
97func (r *REST) New() runtime.Object {
98	return r.store.New()
99}
100
101func (r *REST) NewList() runtime.Object {
102	return r.store.NewList()
103}
104
105func (r *REST) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
106	return r.store.List(ctx, options)
107}
108
109func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
110	return r.store.Create(ctx, obj, createValidation, options)
111}
112
113func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
114	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options)
115}
116
117func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
118	return r.store.Get(ctx, name, options)
119}
120
121func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
122	return r.store.Watch(ctx, options)
123}
124
125// Delete enforces life-cycle rules for namespace termination
126func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
127	nsObj, err := r.Get(ctx, name, &metav1.GetOptions{})
128	if err != nil {
129		return nil, false, err
130	}
131
132	namespace := nsObj.(*api.Namespace)
133
134	// Ensure we have a UID precondition
135	if options == nil {
136		options = metav1.NewDeleteOptions(0)
137	}
138	if options.Preconditions == nil {
139		options.Preconditions = &metav1.Preconditions{}
140	}
141	if options.Preconditions.UID == nil {
142		options.Preconditions.UID = &namespace.UID
143	} else if *options.Preconditions.UID != namespace.UID {
144		err = apierrors.NewConflict(
145			api.Resource("namespaces"),
146			name,
147			fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *options.Preconditions.UID, namespace.UID),
148		)
149		return nil, false, err
150	}
151	if options.Preconditions.ResourceVersion != nil && *options.Preconditions.ResourceVersion != namespace.ResourceVersion {
152		err = apierrors.NewConflict(
153			api.Resource("namespaces"),
154			name,
155			fmt.Errorf("Precondition failed: ResourceVersion in precondition: %v, ResourceVersion in object meta: %v", *options.Preconditions.ResourceVersion, namespace.ResourceVersion),
156		)
157		return nil, false, err
158	}
159
160	// upon first request to delete, we switch the phase to start namespace termination
161	// TODO: enhance graceful deletion's calls to DeleteStrategy to allow phase change and finalizer patterns
162	if namespace.DeletionTimestamp.IsZero() {
163		key, err := r.store.KeyFunc(ctx, name)
164		if err != nil {
165			return nil, false, err
166		}
167
168		preconditions := storage.Preconditions{UID: options.Preconditions.UID, ResourceVersion: options.Preconditions.ResourceVersion}
169
170		out := r.store.NewFunc()
171		err = r.store.Storage.GuaranteedUpdate(
172			ctx, key, out, false, &preconditions,
173			storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) {
174				existingNamespace, ok := existing.(*api.Namespace)
175				if !ok {
176					// wrong type
177					return nil, fmt.Errorf("expected *api.Namespace, got %v", existing)
178				}
179				if err := deleteValidation(ctx, existingNamespace); err != nil {
180					return nil, err
181				}
182				// Set the deletion timestamp if needed
183				if existingNamespace.DeletionTimestamp.IsZero() {
184					now := metav1.Now()
185					existingNamespace.DeletionTimestamp = &now
186				}
187				// Set the namespace phase to terminating, if needed
188				if existingNamespace.Status.Phase != api.NamespaceTerminating {
189					existingNamespace.Status.Phase = api.NamespaceTerminating
190				}
191
192				// the current finalizers which are on namespace
193				currentFinalizers := map[string]bool{}
194				for _, f := range existingNamespace.Finalizers {
195					currentFinalizers[f] = true
196				}
197				// the finalizers we should ensure on namespace
198				shouldHaveFinalizers := map[string]bool{
199					metav1.FinalizerOrphanDependents: shouldHaveOrphanFinalizer(options, currentFinalizers[metav1.FinalizerOrphanDependents]),
200					metav1.FinalizerDeleteDependents: shouldHaveDeleteDependentsFinalizer(options, currentFinalizers[metav1.FinalizerDeleteDependents]),
201				}
202				// determine whether there are changes
203				changeNeeded := false
204				for finalizer, shouldHave := range shouldHaveFinalizers {
205					changeNeeded = currentFinalizers[finalizer] != shouldHave || changeNeeded
206					if shouldHave {
207						currentFinalizers[finalizer] = true
208					} else {
209						delete(currentFinalizers, finalizer)
210					}
211				}
212				// make the changes if needed
213				if changeNeeded {
214					newFinalizers := []string{}
215					for f := range currentFinalizers {
216						newFinalizers = append(newFinalizers, f)
217					}
218					existingNamespace.Finalizers = newFinalizers
219				}
220				return existingNamespace, nil
221			}),
222			dryrun.IsDryRun(options.DryRun),
223			nil,
224		)
225
226		if err != nil {
227			err = storageerr.InterpretGetError(err, api.Resource("namespaces"), name)
228			err = storageerr.InterpretUpdateError(err, api.Resource("namespaces"), name)
229			if _, ok := err.(*apierrors.StatusError); !ok {
230				err = apierrors.NewInternalError(err)
231			}
232			return nil, false, err
233		}
234
235		return out, false, nil
236	}
237
238	// prior to final deletion, we must ensure that finalizers is empty
239	if len(namespace.Spec.Finalizers) != 0 {
240		return namespace, false, nil
241	}
242	return r.store.Delete(ctx, name, deleteValidation, options)
243}
244
245// ShouldDeleteNamespaceDuringUpdate adds namespace-specific spec.finalizer checks on top of the default generic ShouldDeleteDuringUpdate behavior
246func ShouldDeleteNamespaceDuringUpdate(ctx context.Context, key string, obj, existing runtime.Object) bool {
247	ns, ok := obj.(*api.Namespace)
248	if !ok {
249		utilruntime.HandleError(fmt.Errorf("unexpected type %T", obj))
250		return false
251	}
252	return len(ns.Spec.Finalizers) == 0 && genericregistry.ShouldDeleteDuringUpdate(ctx, key, obj, existing)
253}
254
255func shouldHaveOrphanFinalizer(options *metav1.DeleteOptions, haveOrphanFinalizer bool) bool {
256	//lint:ignore SA1019 backwards compatibility
257	if options.OrphanDependents != nil {
258		//lint:ignore SA1019 backwards compatibility
259		return *options.OrphanDependents
260	}
261	if options.PropagationPolicy != nil {
262		return *options.PropagationPolicy == metav1.DeletePropagationOrphan
263	}
264	return haveOrphanFinalizer
265}
266
267func shouldHaveDeleteDependentsFinalizer(options *metav1.DeleteOptions, haveDeleteDependentsFinalizer bool) bool {
268	//lint:ignore SA1019 backwards compatibility
269	if options.OrphanDependents != nil {
270		//lint:ignore SA1019 backwards compatibility
271		return *options.OrphanDependents == false
272	}
273	if options.PropagationPolicy != nil {
274		return *options.PropagationPolicy == metav1.DeletePropagationForeground
275	}
276	return haveDeleteDependentsFinalizer
277}
278
279func (e *REST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
280	return e.store.ConvertToTable(ctx, object, tableOptions)
281}
282
283// Implement ShortNamesProvider
284var _ rest.ShortNamesProvider = &REST{}
285
286// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
287func (r *REST) ShortNames() []string {
288	return []string{"ns"}
289}
290
291var _ rest.StorageVersionProvider = &REST{}
292
293func (r *REST) StorageVersion() runtime.GroupVersioner {
294	return r.store.StorageVersion()
295}
296
297// GetResetFields implements rest.ResetFieldsStrategy
298func (r *REST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
299	return r.store.GetResetFields()
300}
301func (r *StatusREST) New() runtime.Object {
302	return r.store.New()
303}
304
305// Get retrieves the object from the storage. It is required to support Patch.
306func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
307	return r.store.Get(ctx, name, options)
308}
309
310// Update alters the status subset of an object.
311func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
312	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
313	// subresources should never allow create on update.
314	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
315}
316
317// GetResetFields implements rest.ResetFieldsStrategy
318func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
319	return r.store.GetResetFields()
320}
321func (r *FinalizeREST) New() runtime.Object {
322	return r.store.New()
323}
324
325// Update alters the status finalizers subset of an object.
326func (r *FinalizeREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
327	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
328	// subresources should never allow create on update.
329	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
330}
331
332// GetResetFields implements rest.ResetFieldsStrategy
333func (r *FinalizeREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
334	return r.store.GetResetFields()
335}
336