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