k8s.io/kubernetes@v1.29.3/pkg/registry/core/namespace/storage/storage.go (about) 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 }