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 }