knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/client/injection/kube/reconciler/core/v1/secret/reconciler.go (about)

     1  /*
     2  Copyright 2022 The Knative 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  // Code generated by injection-gen. DO NOT EDIT.
    18  
    19  package secret
    20  
    21  import (
    22  	context "context"
    23  	json "encoding/json"
    24  	fmt "fmt"
    25  
    26  	zap "go.uber.org/zap"
    27  	v1 "k8s.io/api/core/v1"
    28  	errors "k8s.io/apimachinery/pkg/api/errors"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	labels "k8s.io/apimachinery/pkg/labels"
    31  	types "k8s.io/apimachinery/pkg/types"
    32  	sets "k8s.io/apimachinery/pkg/util/sets"
    33  	kubernetes "k8s.io/client-go/kubernetes"
    34  	scheme "k8s.io/client-go/kubernetes/scheme"
    35  	corev1 "k8s.io/client-go/listers/core/v1"
    36  	record "k8s.io/client-go/tools/record"
    37  	controller "knative.dev/pkg/controller"
    38  	logging "knative.dev/pkg/logging"
    39  	reconciler "knative.dev/pkg/reconciler"
    40  )
    41  
    42  // Interface defines the strongly typed interfaces to be implemented by a
    43  // controller reconciling v1.Secret.
    44  type Interface interface {
    45  	// ReconcileKind implements custom logic to reconcile v1.Secret. Any changes
    46  	// to the objects .Status or .Finalizers will be propagated to the stored
    47  	// object. It is recommended that implementors do not call any update calls
    48  	// for the Kind inside of ReconcileKind, it is the responsibility of the calling
    49  	// controller to propagate those properties. The resource passed to ReconcileKind
    50  	// will always have an empty deletion timestamp.
    51  	ReconcileKind(ctx context.Context, o *v1.Secret) reconciler.Event
    52  }
    53  
    54  // Finalizer defines the strongly typed interfaces to be implemented by a
    55  // controller finalizing v1.Secret.
    56  type Finalizer interface {
    57  	// FinalizeKind implements custom logic to finalize v1.Secret. Any changes
    58  	// to the objects .Status or .Finalizers will be ignored. Returning a nil or
    59  	// Normal type reconciler.Event will allow the finalizer to be deleted on
    60  	// the resource. The resource passed to FinalizeKind will always have a set
    61  	// deletion timestamp.
    62  	FinalizeKind(ctx context.Context, o *v1.Secret) reconciler.Event
    63  }
    64  
    65  // ReadOnlyInterface defines the strongly typed interfaces to be implemented by a
    66  // controller reconciling v1.Secret if they want to process resources for which
    67  // they are not the leader.
    68  type ReadOnlyInterface interface {
    69  	// ObserveKind implements logic to observe v1.Secret.
    70  	// This method should not write to the API.
    71  	ObserveKind(ctx context.Context, o *v1.Secret) reconciler.Event
    72  }
    73  
    74  type doReconcile func(ctx context.Context, o *v1.Secret) reconciler.Event
    75  
    76  // reconcilerImpl implements controller.Reconciler for v1.Secret resources.
    77  type reconcilerImpl struct {
    78  	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware.
    79  	reconciler.LeaderAwareFuncs
    80  
    81  	// Client is used to write back status updates.
    82  	Client kubernetes.Interface
    83  
    84  	// Listers index properties about resources.
    85  	Lister corev1.SecretLister
    86  
    87  	// Recorder is an event recorder for recording Event resources to the
    88  	// Kubernetes API.
    89  	Recorder record.EventRecorder
    90  
    91  	// configStore allows for decorating a context with config maps.
    92  	// +optional
    93  	configStore reconciler.ConfigStore
    94  
    95  	// reconciler is the implementation of the business logic of the resource.
    96  	reconciler Interface
    97  
    98  	// finalizerName is the name of the finalizer to reconcile.
    99  	finalizerName string
   100  
   101  	// useServerSideApplyForFinalizers configures whether to use server-side apply for finalizer management
   102  	useServerSideApplyForFinalizers bool
   103  
   104  	// finalizerFieldManager is the field manager name for server-side apply of finalizers
   105  	finalizerFieldManager string
   106  
   107  	// forceApplyFinalizers configures whether to force server-side apply for finalizers
   108  	forceApplyFinalizers bool
   109  }
   110  
   111  // Check that our Reconciler implements controller.Reconciler.
   112  var _ controller.Reconciler = (*reconcilerImpl)(nil)
   113  
   114  // Check that our generated Reconciler is always LeaderAware.
   115  var _ reconciler.LeaderAware = (*reconcilerImpl)(nil)
   116  
   117  func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubernetes.Interface, lister corev1.SecretLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler {
   118  	// Check the options function input. It should be 0 or 1.
   119  	if len(options) > 1 {
   120  		logger.Fatal("Up to one options struct is supported, found: ", len(options))
   121  	}
   122  
   123  	// Fail fast when users inadvertently implement the other LeaderAware interface.
   124  	// For the typed reconcilers, Promote shouldn't take any arguments.
   125  	if _, ok := r.(reconciler.LeaderAware); ok {
   126  		logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r)
   127  	}
   128  
   129  	rec := &reconcilerImpl{
   130  		LeaderAwareFuncs: reconciler.LeaderAwareFuncs{
   131  			PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error {
   132  				all, err := lister.List(labels.Everything())
   133  				if err != nil {
   134  					return err
   135  				}
   136  				for _, elt := range all {
   137  					// TODO: Consider letting users specify a filter in options.
   138  					enq(bkt, types.NamespacedName{
   139  						Namespace: elt.GetNamespace(),
   140  						Name:      elt.GetName(),
   141  					})
   142  				}
   143  				return nil
   144  			},
   145  		},
   146  		Client:        client,
   147  		Lister:        lister,
   148  		Recorder:      recorder,
   149  		reconciler:    r,
   150  		finalizerName: defaultFinalizerName,
   151  	}
   152  
   153  	for _, opts := range options {
   154  		if opts.ConfigStore != nil {
   155  			rec.configStore = opts.ConfigStore
   156  		}
   157  		if opts.FinalizerName != "" {
   158  			rec.finalizerName = opts.FinalizerName
   159  		}
   160  		if opts.DemoteFunc != nil {
   161  			rec.DemoteFunc = opts.DemoteFunc
   162  		}
   163  		if opts.UseServerSideApplyForFinalizers {
   164  			if opts.FinalizerFieldManager == "" {
   165  				logger.Fatal("FinalizerFieldManager must be provided when UseServerSideApplyForFinalizers is enabled")
   166  			}
   167  			rec.useServerSideApplyForFinalizers = true
   168  			rec.finalizerFieldManager = opts.FinalizerFieldManager
   169  			rec.forceApplyFinalizers = opts.ForceApplyFinalizers
   170  		}
   171  	}
   172  
   173  	return rec
   174  }
   175  
   176  // Reconcile implements controller.Reconciler
   177  func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
   178  	logger := logging.FromContext(ctx)
   179  
   180  	// Initialize the reconciler state. This will convert the namespace/name
   181  	// string into a distinct namespace and name, determine if this instance of
   182  	// the reconciler is the leader, and any additional interfaces implemented
   183  	// by the reconciler. Returns an error is the resource key is invalid.
   184  	s, err := newState(key, r)
   185  	if err != nil {
   186  		logger.Error("Invalid resource key: ", key)
   187  		return nil
   188  	}
   189  
   190  	// If we are not the leader, and we don't implement either ReadOnly
   191  	// observer interfaces, then take a fast-path out.
   192  	if s.isNotLeaderNorObserver() {
   193  		return controller.NewSkipKey(key)
   194  	}
   195  
   196  	// If configStore is set, attach the frozen configuration to the context.
   197  	if r.configStore != nil {
   198  		ctx = r.configStore.ToContext(ctx)
   199  	}
   200  
   201  	// Add the recorder to context.
   202  	ctx = controller.WithEventRecorder(ctx, r.Recorder)
   203  
   204  	// Get the resource with this namespace/name.
   205  
   206  	getter := r.Lister.Secrets(s.namespace)
   207  
   208  	original, err := getter.Get(s.name)
   209  
   210  	if errors.IsNotFound(err) {
   211  		// The resource may no longer exist, in which case we stop processing and call
   212  		// the ObserveDeletion handler if appropriate.
   213  		logger.Debugf("Resource %q no longer exists", key)
   214  		if del, ok := r.reconciler.(reconciler.OnDeletionInterface); ok {
   215  			return del.ObserveDeletion(ctx, types.NamespacedName{
   216  				Namespace: s.namespace,
   217  				Name:      s.name,
   218  			})
   219  		}
   220  		return nil
   221  	} else if err != nil {
   222  		return err
   223  	}
   224  
   225  	// Don't modify the informers copy.
   226  	resource := original.DeepCopy()
   227  
   228  	var reconcileEvent reconciler.Event
   229  
   230  	name, do := s.reconcileMethodFor(resource)
   231  	// Append the target method to the logger.
   232  	logger = logger.With(zap.String("targetMethod", name))
   233  	switch name {
   234  	case reconciler.DoReconcileKind:
   235  		// Set and update the finalizer on resource if r.reconciler
   236  		// implements Finalizer.
   237  		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
   238  			return fmt.Errorf("failed to set finalizers: %w", err)
   239  		}
   240  
   241  		// Reconcile this copy of the resource and then write back any status
   242  		// updates regardless of whether the reconciliation errored out.
   243  		reconcileEvent = do(ctx, resource)
   244  
   245  	case reconciler.DoFinalizeKind:
   246  		// For finalizing reconcilers, if this resource being marked for deletion
   247  		// and reconciled cleanly (nil or normal event), remove the finalizer.
   248  		reconcileEvent = do(ctx, resource)
   249  
   250  		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
   251  			return fmt.Errorf("failed to clear finalizers: %w", err)
   252  		}
   253  
   254  	case reconciler.DoObserveKind:
   255  		// Observe any changes to this resource, since we are not the leader.
   256  		reconcileEvent = do(ctx, resource)
   257  
   258  	}
   259  
   260  	// Report the reconciler event, if any.
   261  	if reconcileEvent != nil {
   262  		var event *reconciler.ReconcilerEvent
   263  		if reconciler.EventAs(reconcileEvent, &event) {
   264  			logger.Infow("Returned an event", zap.Any("event", reconcileEvent))
   265  			r.Recorder.Event(resource, event.EventType, event.Reason, event.Error())
   266  
   267  			// the event was wrapped inside an error, consider the reconciliation as failed
   268  			if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent {
   269  				return reconcileEvent
   270  			}
   271  			return nil
   272  		}
   273  
   274  		if controller.IsSkipKey(reconcileEvent) {
   275  			// This is a wrapped error, don't emit an event.
   276  		} else if ok, _ := controller.IsRequeueKey(reconcileEvent); ok {
   277  			// This is a wrapped error, don't emit an event.
   278  		} else {
   279  			logger.Errorw("Returned an error", zap.Error(reconcileEvent))
   280  			r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error())
   281  		}
   282  		return reconcileEvent
   283  	}
   284  
   285  	return nil
   286  }
   287  
   288  // updateFinalizersFiltered will update the Finalizers of the resource.
   289  // TODO: this method could be generic and sync all finalizers. For now it only
   290  // updates defaultFinalizerName or its override.
   291  func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1.Secret, desiredFinalizers sets.Set[string]) (*v1.Secret, error) {
   292  	if r.useServerSideApplyForFinalizers {
   293  		return r.updateFinalizersFilteredServerSideApply(ctx, resource, desiredFinalizers)
   294  	}
   295  	return r.updateFinalizersFilteredMergePatch(ctx, resource, desiredFinalizers)
   296  }
   297  
   298  // updateFinalizersFilteredServerSideApply uses server-side apply to manage only this controller's finalizer.
   299  func (r *reconcilerImpl) updateFinalizersFilteredServerSideApply(ctx context.Context, resource *v1.Secret, desiredFinalizers sets.Set[string]) (*v1.Secret, error) {
   300  	// Check if we need to do anything
   301  	existingFinalizers := sets.New[string](resource.Finalizers...)
   302  
   303  	var finalizers []string
   304  	if desiredFinalizers.Has(r.finalizerName) {
   305  		if existingFinalizers.Has(r.finalizerName) {
   306  			// Nothing to do.
   307  			return resource, nil
   308  		}
   309  		// Apply configuration with only our finalizer to add it.
   310  		finalizers = []string{r.finalizerName}
   311  	} else {
   312  		if !existingFinalizers.Has(r.finalizerName) {
   313  			// Nothing to do.
   314  			return resource, nil
   315  		}
   316  		// For removal, we apply an empty configuration for our finalizer field manager.
   317  		// This effectively removes our finalizer while preserving others.
   318  		finalizers = []string{} // Empty array removes our managed finalizers
   319  	}
   320  
   321  	// Determine GVK
   322  	gvks, _, err := scheme.Scheme.ObjectKinds(resource)
   323  	if err != nil || len(gvks) == 0 {
   324  		return resource, fmt.Errorf("failed to determine GVK for resource: %w", err)
   325  	}
   326  	gvk := gvks[0]
   327  
   328  	// Create apply configuration
   329  	applyConfig := map[string]interface{}{
   330  		"apiVersion": gvk.GroupVersion().String(),
   331  		"kind":       gvk.Kind,
   332  		"metadata": map[string]interface{}{
   333  			"name":       resource.Name,
   334  			"uid":        resource.UID,
   335  			"finalizers": finalizers,
   336  		},
   337  	}
   338  
   339  	applyConfig["metadata"].(map[string]interface{})["namespace"] = resource.Namespace
   340  
   341  	patch, err := json.Marshal(applyConfig)
   342  	if err != nil {
   343  		return resource, err
   344  	}
   345  
   346  	patcher := r.Client.CoreV1().Secrets(resource.Namespace)
   347  
   348  	patchOpts := metav1.PatchOptions{
   349  		FieldManager: r.finalizerFieldManager,
   350  		Force:        &r.forceApplyFinalizers,
   351  	}
   352  
   353  	updated, err := patcher.Patch(ctx, resource.Name, types.ApplyPatchType, patch, patchOpts)
   354  	if err != nil {
   355  		r.Recorder.Eventf(resource, v1.EventTypeWarning, "FinalizerUpdateFailed",
   356  			"Failed to update finalizers for %q via server-side apply: %v", resource.Name, err)
   357  	} else {
   358  		r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate",
   359  			"Updated finalizers for %q via server-side apply", resource.GetName())
   360  	}
   361  	return updated, err
   362  }
   363  
   364  // updateFinalizersFilteredMergePatch uses merge patch to manage finalizers (legacy behavior).
   365  func (r *reconcilerImpl) updateFinalizersFilteredMergePatch(ctx context.Context, resource *v1.Secret, desiredFinalizers sets.Set[string]) (*v1.Secret, error) {
   366  	// Don't modify the informers copy.
   367  	existing := resource.DeepCopy()
   368  
   369  	var finalizers []string
   370  
   371  	// If there's nothing to update, just return.
   372  	existingFinalizers := sets.New[string](existing.Finalizers...)
   373  
   374  	if desiredFinalizers.Has(r.finalizerName) {
   375  		if existingFinalizers.Has(r.finalizerName) {
   376  			// Nothing to do.
   377  			return resource, nil
   378  		}
   379  		// Add the finalizer.
   380  		finalizers = append(existing.Finalizers, r.finalizerName)
   381  	} else {
   382  		if !existingFinalizers.Has(r.finalizerName) {
   383  			// Nothing to do.
   384  			return resource, nil
   385  		}
   386  		// Remove the finalizer.
   387  		existingFinalizers.Delete(r.finalizerName)
   388  		finalizers = sets.List(existingFinalizers)
   389  	}
   390  
   391  	mergePatch := map[string]interface{}{
   392  		"metadata": map[string]interface{}{
   393  			"finalizers":      finalizers,
   394  			"resourceVersion": existing.ResourceVersion,
   395  		},
   396  	}
   397  
   398  	patch, err := json.Marshal(mergePatch)
   399  	if err != nil {
   400  		return resource, err
   401  	}
   402  
   403  	patcher := r.Client.CoreV1().Secrets(resource.Namespace)
   404  
   405  	resourceName := resource.Name
   406  	updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{})
   407  	if err != nil {
   408  		r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed",
   409  			"Failed to update finalizers for %q: %v", resourceName, err)
   410  	} else {
   411  		r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate",
   412  			"Updated %q finalizers", resource.GetName())
   413  	}
   414  	return updated, err
   415  }
   416  
   417  func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1.Secret) (*v1.Secret, error) {
   418  	if _, ok := r.reconciler.(Finalizer); !ok {
   419  		return resource, nil
   420  	}
   421  
   422  	finalizers := sets.New[string](resource.Finalizers...)
   423  
   424  	// If this resource is not being deleted, mark the finalizer.
   425  	if resource.GetDeletionTimestamp().IsZero() {
   426  		finalizers.Insert(r.finalizerName)
   427  	}
   428  
   429  	// Synchronize the finalizers filtered by r.finalizerName.
   430  	return r.updateFinalizersFiltered(ctx, resource, finalizers)
   431  }
   432  
   433  func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1.Secret, reconcileEvent reconciler.Event) (*v1.Secret, error) {
   434  	if _, ok := r.reconciler.(Finalizer); !ok {
   435  		return resource, nil
   436  	}
   437  	if resource.GetDeletionTimestamp().IsZero() {
   438  		return resource, nil
   439  	}
   440  
   441  	finalizers := sets.New[string](resource.Finalizers...)
   442  
   443  	if reconcileEvent != nil {
   444  		var event *reconciler.ReconcilerEvent
   445  		if reconciler.EventAs(reconcileEvent, &event) {
   446  			if event.EventType == v1.EventTypeNormal {
   447  				finalizers.Delete(r.finalizerName)
   448  			}
   449  		}
   450  	} else {
   451  		finalizers.Delete(r.finalizerName)
   452  	}
   453  
   454  	// Synchronize the finalizers filtered by r.finalizerName.
   455  	updated, err := r.updateFinalizersFiltered(ctx, resource, finalizers)
   456  	if err != nil {
   457  		// Check if the resource still exists by querying the API server to avoid logging errors
   458  		// when reconciling stale object from cache while the object is actually deleted.
   459  		logger := logging.FromContext(ctx)
   460  
   461  		getter := r.Client.CoreV1().Secrets(resource.Namespace)
   462  
   463  		_, getErr := getter.Get(ctx, resource.Name, metav1.GetOptions{})
   464  		if errors.IsNotFound(getErr) {
   465  			// Resource no longer exists, which could happen during deletion
   466  			logger.Debugw("Resource no longer exists while clearing finalizers",
   467  				"resource", resource.GetName(),
   468  				"namespace", resource.GetNamespace(),
   469  				"originalError", err)
   470  			// Return the original resource since the finalizer clearing is effectively complete
   471  			return resource, nil
   472  		}
   473  
   474  		// For other errors, return the original error
   475  		return updated, err
   476  	}
   477  
   478  	return updated, nil
   479  }