...

Source file src/k8s.io/kubernetes/pkg/registry/core/namespace/storage/storage.go

Documentation: k8s.io/kubernetes/pkg/registry/core/namespace/storage

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package storage
    18  
    19  import (
    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
    44  type 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.
    50  type StatusREST struct {
    51  	store *genericregistry.Store
    52  }
    53  
    54  // FinalizeREST implements the REST endpoint for finalizing a namespace.
    55  type FinalizeREST struct {
    56  	store *genericregistry.Store
    57  }
    58  
    59  // NewREST returns a RESTStorage object that will work against namespaces.
    60  func 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  		SingularQualifiedResource: api.Resource("namespace"),
    67  
    68  		CreateStrategy:      namespace.Strategy,
    69  		UpdateStrategy:      namespace.Strategy,
    70  		DeleteStrategy:      namespace.Strategy,
    71  		ResetFieldsStrategy: namespace.Strategy,
    72  		ReturnDeletedObject: true,
    73  
    74  		ShouldDeleteDuringUpdate: ShouldDeleteNamespaceDuringUpdate,
    75  
    76  		TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
    77  	}
    78  	options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: namespace.GetAttrs}
    79  	if err := store.CompleteWithOptions(options); err != nil {
    80  		return nil, nil, nil, err
    81  	}
    82  
    83  	statusStore := *store
    84  	statusStore.UpdateStrategy = namespace.StatusStrategy
    85  	statusStore.ResetFieldsStrategy = namespace.StatusStrategy
    86  
    87  	finalizeStore := *store
    88  	finalizeStore.UpdateStrategy = namespace.FinalizeStrategy
    89  	finalizeStore.ResetFieldsStrategy = namespace.FinalizeStrategy
    90  
    91  	return &REST{store: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore}, nil
    92  }
    93  
    94  func (r *REST) NamespaceScoped() bool {
    95  	return r.store.NamespaceScoped()
    96  }
    97  
    98  var _ rest.SingularNameProvider = &REST{}
    99  
   100  func (r *REST) GetSingularName() string {
   101  	return r.store.GetSingularName()
   102  }
   103  
   104  func (r *REST) New() runtime.Object {
   105  	return r.store.New()
   106  }
   107  
   108  // Destroy cleans up resources on shutdown.
   109  func (r *REST) Destroy() {
   110  	r.store.Destroy()
   111  }
   112  
   113  func (r *REST) NewList() runtime.Object {
   114  	return r.store.NewList()
   115  }
   116  
   117  func (r *REST) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
   118  	return r.store.List(ctx, options)
   119  }
   120  
   121  func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
   122  	return r.store.Create(ctx, obj, createValidation, options)
   123  }
   124  
   125  func (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) {
   126  	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options)
   127  }
   128  
   129  func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
   130  	return r.store.Get(ctx, name, options)
   131  }
   132  
   133  func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
   134  	return r.store.Watch(ctx, options)
   135  }
   136  
   137  // Delete enforces life-cycle rules for namespace termination
   138  func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
   139  	nsObj, err := r.Get(ctx, name, &metav1.GetOptions{})
   140  	if err != nil {
   141  		return nil, false, err
   142  	}
   143  
   144  	namespace := nsObj.(*api.Namespace)
   145  
   146  	// Ensure we have a UID precondition
   147  	if options == nil {
   148  		options = metav1.NewDeleteOptions(0)
   149  	}
   150  	if options.Preconditions == nil {
   151  		options.Preconditions = &metav1.Preconditions{}
   152  	}
   153  	if options.Preconditions.UID == nil {
   154  		options.Preconditions.UID = &namespace.UID
   155  	} else if *options.Preconditions.UID != namespace.UID {
   156  		err = apierrors.NewConflict(
   157  			api.Resource("namespaces"),
   158  			name,
   159  			fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *options.Preconditions.UID, namespace.UID),
   160  		)
   161  		return nil, false, err
   162  	}
   163  	if options.Preconditions.ResourceVersion != nil && *options.Preconditions.ResourceVersion != namespace.ResourceVersion {
   164  		err = apierrors.NewConflict(
   165  			api.Resource("namespaces"),
   166  			name,
   167  			fmt.Errorf("Precondition failed: ResourceVersion in precondition: %v, ResourceVersion in object meta: %v", *options.Preconditions.ResourceVersion, namespace.ResourceVersion),
   168  		)
   169  		return nil, false, err
   170  	}
   171  
   172  	// upon first request to delete, we switch the phase to start namespace termination
   173  	// TODO: enhance graceful deletion's calls to DeleteStrategy to allow phase change and finalizer patterns
   174  	if namespace.DeletionTimestamp.IsZero() {
   175  		key, err := r.store.KeyFunc(ctx, name)
   176  		if err != nil {
   177  			return nil, false, err
   178  		}
   179  
   180  		preconditions := storage.Preconditions{UID: options.Preconditions.UID, ResourceVersion: options.Preconditions.ResourceVersion}
   181  
   182  		out := r.store.NewFunc()
   183  		err = r.store.Storage.GuaranteedUpdate(
   184  			ctx, key, out, false, &preconditions,
   185  			storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) {
   186  				existingNamespace, ok := existing.(*api.Namespace)
   187  				if !ok {
   188  					// wrong type
   189  					return nil, fmt.Errorf("expected *api.Namespace, got %v", existing)
   190  				}
   191  				if err := deleteValidation(ctx, existingNamespace); err != nil {
   192  					return nil, err
   193  				}
   194  				// Set the deletion timestamp if needed
   195  				if existingNamespace.DeletionTimestamp.IsZero() {
   196  					now := metav1.Now()
   197  					existingNamespace.DeletionTimestamp = &now
   198  				}
   199  				// Set the namespace phase to terminating, if needed
   200  				if existingNamespace.Status.Phase != api.NamespaceTerminating {
   201  					existingNamespace.Status.Phase = api.NamespaceTerminating
   202  				}
   203  
   204  				// the current finalizers which are on namespace
   205  				currentFinalizers := map[string]bool{}
   206  				for _, f := range existingNamespace.Finalizers {
   207  					currentFinalizers[f] = true
   208  				}
   209  				// the finalizers we should ensure on namespace
   210  				shouldHaveFinalizers := map[string]bool{
   211  					metav1.FinalizerOrphanDependents: shouldHaveOrphanFinalizer(options, currentFinalizers[metav1.FinalizerOrphanDependents]),
   212  					metav1.FinalizerDeleteDependents: shouldHaveDeleteDependentsFinalizer(options, currentFinalizers[metav1.FinalizerDeleteDependents]),
   213  				}
   214  				// determine whether there are changes
   215  				changeNeeded := false
   216  				for finalizer, shouldHave := range shouldHaveFinalizers {
   217  					changeNeeded = currentFinalizers[finalizer] != shouldHave || changeNeeded
   218  					if shouldHave {
   219  						currentFinalizers[finalizer] = true
   220  					} else {
   221  						delete(currentFinalizers, finalizer)
   222  					}
   223  				}
   224  				// make the changes if needed
   225  				if changeNeeded {
   226  					newFinalizers := []string{}
   227  					for f := range currentFinalizers {
   228  						newFinalizers = append(newFinalizers, f)
   229  					}
   230  					existingNamespace.Finalizers = newFinalizers
   231  				}
   232  				return existingNamespace, nil
   233  			}),
   234  			dryrun.IsDryRun(options.DryRun),
   235  			nil,
   236  		)
   237  
   238  		if err != nil {
   239  			err = storageerr.InterpretGetError(err, api.Resource("namespaces"), name)
   240  			err = storageerr.InterpretUpdateError(err, api.Resource("namespaces"), name)
   241  			if _, ok := err.(*apierrors.StatusError); !ok {
   242  				err = apierrors.NewInternalError(err)
   243  			}
   244  			return nil, false, err
   245  		}
   246  
   247  		return out, false, nil
   248  	}
   249  
   250  	// prior to final deletion, we must ensure that finalizers is empty
   251  	if len(namespace.Spec.Finalizers) != 0 {
   252  		return namespace, false, nil
   253  	}
   254  	return r.store.Delete(ctx, name, deleteValidation, options)
   255  }
   256  
   257  // ShouldDeleteNamespaceDuringUpdate adds namespace-specific spec.finalizer checks on top of the default generic ShouldDeleteDuringUpdate behavior
   258  func ShouldDeleteNamespaceDuringUpdate(ctx context.Context, key string, obj, existing runtime.Object) bool {
   259  	ns, ok := obj.(*api.Namespace)
   260  	if !ok {
   261  		utilruntime.HandleError(fmt.Errorf("unexpected type %T", obj))
   262  		return false
   263  	}
   264  	return len(ns.Spec.Finalizers) == 0 && genericregistry.ShouldDeleteDuringUpdate(ctx, key, obj, existing)
   265  }
   266  
   267  func shouldHaveOrphanFinalizer(options *metav1.DeleteOptions, haveOrphanFinalizer bool) bool {
   268  	//nolint:staticcheck // SA1019 backwards compatibility
   269  	if options.OrphanDependents != nil {
   270  		return *options.OrphanDependents
   271  	}
   272  	if options.PropagationPolicy != nil {
   273  		return *options.PropagationPolicy == metav1.DeletePropagationOrphan
   274  	}
   275  	return haveOrphanFinalizer
   276  }
   277  
   278  func shouldHaveDeleteDependentsFinalizer(options *metav1.DeleteOptions, haveDeleteDependentsFinalizer bool) bool {
   279  	//nolint:staticcheck // SA1019 backwards compatibility
   280  	if options.OrphanDependents != nil {
   281  		return *options.OrphanDependents == false
   282  	}
   283  	if options.PropagationPolicy != nil {
   284  		return *options.PropagationPolicy == metav1.DeletePropagationForeground
   285  	}
   286  	return haveDeleteDependentsFinalizer
   287  }
   288  
   289  func (e *REST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
   290  	return e.store.ConvertToTable(ctx, object, tableOptions)
   291  }
   292  
   293  // Implement ShortNamesProvider
   294  var _ rest.ShortNamesProvider = &REST{}
   295  
   296  // ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
   297  func (r *REST) ShortNames() []string {
   298  	return []string{"ns"}
   299  }
   300  
   301  var _ rest.StorageVersionProvider = &REST{}
   302  
   303  func (r *REST) StorageVersion() runtime.GroupVersioner {
   304  	return r.store.StorageVersion()
   305  }
   306  
   307  // GetResetFields implements rest.ResetFieldsStrategy
   308  func (r *REST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   309  	return r.store.GetResetFields()
   310  }
   311  func (r *StatusREST) New() runtime.Object {
   312  	return r.store.New()
   313  }
   314  
   315  // Destroy cleans up resources on shutdown.
   316  func (r *StatusREST) Destroy() {
   317  	// Given that underlying store is shared with REST,
   318  	// we don't destroy it here explicitly.
   319  }
   320  
   321  // Get retrieves the object from the storage. It is required to support Patch.
   322  func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
   323  	return r.store.Get(ctx, name, options)
   324  }
   325  
   326  // Update alters the status subset of an object.
   327  func (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) {
   328  	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
   329  	// subresources should never allow create on update.
   330  	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
   331  }
   332  
   333  // GetResetFields implements rest.ResetFieldsStrategy
   334  func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   335  	return r.store.GetResetFields()
   336  }
   337  
   338  func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
   339  	return r.store.ConvertToTable(ctx, object, tableOptions)
   340  }
   341  
   342  func (r *FinalizeREST) New() runtime.Object {
   343  	return r.store.New()
   344  }
   345  
   346  // Destroy cleans up resources on shutdown.
   347  func (r *FinalizeREST) Destroy() {
   348  	// Given that underlying store is shared with REST,
   349  	// we don't destroy it here explicitly.
   350  }
   351  
   352  // Update alters the status finalizers subset of an object.
   353  func (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) {
   354  	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
   355  	// subresources should never allow create on update.
   356  	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
   357  }
   358  
   359  // GetResetFields implements rest.ResetFieldsStrategy
   360  func (r *FinalizeREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   361  	return r.store.GetResetFields()
   362  }
   363  

View as plain text