sigs.k8s.io/controller-runtime@v0.18.2/pkg/finalizer/finalizer.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package finalizer 15 16 import ( 17 "context" 18 "fmt" 19 20 kerrors "k8s.io/apimachinery/pkg/util/errors" 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 23 ) 24 25 type finalizers map[string]Finalizer 26 27 // Result struct holds information about what parts of an object were updated by finalizer(s). 28 type Result struct { 29 // Updated will be true if at least one of the object's non-status field 30 // was updated by some registered finalizer. 31 Updated bool 32 // StatusUpdated will be true if at least one of the object's status' fields 33 // was updated by some registered finalizer. 34 StatusUpdated bool 35 } 36 37 // NewFinalizers returns the Finalizers interface. 38 func NewFinalizers() Finalizers { 39 return finalizers{} 40 } 41 42 func (f finalizers) Register(key string, finalizer Finalizer) error { 43 if _, ok := f[key]; ok { 44 return fmt.Errorf("finalizer for key %q already registered", key) 45 } 46 f[key] = finalizer 47 return nil 48 } 49 50 func (f finalizers) Finalize(ctx context.Context, obj client.Object) (Result, error) { 51 var ( 52 res Result 53 errList []error 54 ) 55 res.Updated = false 56 for key, finalizer := range f { 57 if dt := obj.GetDeletionTimestamp(); dt.IsZero() && !controllerutil.ContainsFinalizer(obj, key) { 58 controllerutil.AddFinalizer(obj, key) 59 res.Updated = true 60 } else if !dt.IsZero() && controllerutil.ContainsFinalizer(obj, key) { 61 finalizerRes, err := finalizer.Finalize(ctx, obj) 62 if err != nil { 63 // Even when the finalizer fails, it may need to signal to update the primary 64 // object (e.g. it may set a condition and need a status update). 65 res.Updated = res.Updated || finalizerRes.Updated 66 res.StatusUpdated = res.StatusUpdated || finalizerRes.StatusUpdated 67 errList = append(errList, fmt.Errorf("finalizer %q failed: %w", key, err)) 68 } else { 69 // If the finalizer succeeds, we remove the finalizer from the primary 70 // object's metadata, so we know it will need an update. 71 res.Updated = true 72 controllerutil.RemoveFinalizer(obj, key) 73 // The finalizer may have updated the status too. 74 res.StatusUpdated = res.StatusUpdated || finalizerRes.StatusUpdated 75 } 76 } 77 } 78 return res, kerrors.NewAggregate(errList) 79 }