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