k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/validatingadmissionpolicystatus/controller.go (about) 1 /* 2 Copyright 2023 The Kubernetes 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 package validatingadmissionpolicystatus 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "k8s.io/api/admissionregistration/v1" 25 kerrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 "k8s.io/apimachinery/pkg/util/wait" 29 validatingadmissionpolicy "k8s.io/apiserver/pkg/admission/plugin/policy/validating" 30 admissionregistrationv1apply "k8s.io/client-go/applyconfigurations/admissionregistration/v1" 31 informerv1 "k8s.io/client-go/informers/admissionregistration/v1" 32 admissionregistrationv1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" 33 "k8s.io/client-go/tools/cache" 34 "k8s.io/client-go/util/workqueue" 35 ) 36 37 // ControllerName has "Status" in it to differentiate this controller with the other that runs in API server. 38 const ControllerName = "validatingadmissionpolicy-status" 39 40 // Controller is the ValidatingAdmissionPolicy Status controller that reconciles the Status field of each policy object. 41 // This controller runs type checks against referred types for each policy definition. 42 type Controller struct { 43 policyInformer informerv1.ValidatingAdmissionPolicyInformer 44 policyQueue workqueue.TypedRateLimitingInterface[string] 45 policySynced cache.InformerSynced 46 policyClient admissionregistrationv1.ValidatingAdmissionPolicyInterface 47 48 // typeChecker checks the policy's expressions for type errors. 49 // Type of params is defined in policy.Spec.ParamsKind 50 // Types of object are calculated from policy.Spec.MatchingConstraints 51 typeChecker *validatingadmissionpolicy.TypeChecker 52 } 53 54 func (c *Controller) Run(ctx context.Context, workers int) { 55 defer utilruntime.HandleCrash() 56 57 if !cache.WaitForNamedCacheSync(ControllerName, ctx.Done(), c.policySynced) { 58 return 59 } 60 61 defer c.policyQueue.ShutDown() 62 for i := 0; i < workers; i++ { 63 go wait.UntilWithContext(ctx, c.runWorker, time.Second) 64 } 65 66 <-ctx.Done() 67 } 68 69 func NewController(policyInformer informerv1.ValidatingAdmissionPolicyInformer, policyClient admissionregistrationv1.ValidatingAdmissionPolicyInterface, typeChecker *validatingadmissionpolicy.TypeChecker) (*Controller, error) { 70 c := &Controller{ 71 policyInformer: policyInformer, 72 policyQueue: workqueue.NewTypedRateLimitingQueueWithConfig( 73 workqueue.DefaultTypedControllerRateLimiter[string](), 74 workqueue.TypedRateLimitingQueueConfig[string]{Name: ControllerName}, 75 ), 76 policyClient: policyClient, 77 typeChecker: typeChecker, 78 } 79 reg, err := policyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ 80 AddFunc: func(obj interface{}) { 81 c.enqueuePolicy(obj) 82 }, 83 UpdateFunc: func(oldObj, newObj interface{}) { 84 c.enqueuePolicy(newObj) 85 }, 86 }) 87 if err != nil { 88 return nil, err 89 } 90 c.policySynced = reg.HasSynced 91 return c, nil 92 } 93 94 func (c *Controller) enqueuePolicy(policy any) { 95 if policy, ok := policy.(*v1.ValidatingAdmissionPolicy); ok { 96 // policy objects are cluster-scoped, no point include its namespace. 97 key := policy.ObjectMeta.Name 98 if key == "" { 99 utilruntime.HandleError(fmt.Errorf("cannot get name of object %v", policy)) 100 } 101 c.policyQueue.Add(key) 102 } 103 } 104 105 func (c *Controller) runWorker(ctx context.Context) { 106 for c.processNextWorkItem(ctx) { 107 } 108 } 109 110 func (c *Controller) processNextWorkItem(ctx context.Context) bool { 111 key, shutdown := c.policyQueue.Get() 112 if shutdown { 113 return false 114 } 115 defer c.policyQueue.Done(key) 116 117 err := func() error { 118 policy, err := c.policyInformer.Lister().Get(key) 119 if err != nil { 120 if kerrors.IsNotFound(err) { 121 // If not found, the policy is being deleting, do nothing. 122 return nil 123 } 124 return err 125 } 126 return c.reconcile(ctx, policy) 127 }() 128 129 if err == nil { 130 c.policyQueue.Forget(key) 131 return true 132 } 133 134 utilruntime.HandleError(err) 135 c.policyQueue.AddRateLimited(key) 136 137 return true 138 } 139 140 func (c *Controller) reconcile(ctx context.Context, policy *v1.ValidatingAdmissionPolicy) error { 141 if policy == nil { 142 return nil 143 } 144 if policy.Generation <= policy.Status.ObservedGeneration { 145 return nil 146 } 147 warnings := c.typeChecker.Check(policy) 148 warningsConfig := make([]*admissionregistrationv1apply.ExpressionWarningApplyConfiguration, 0, len(warnings)) 149 for _, warning := range warnings { 150 warningsConfig = append(warningsConfig, admissionregistrationv1apply.ExpressionWarning(). 151 WithFieldRef(warning.FieldRef). 152 WithWarning(warning.Warning)) 153 } 154 applyConfig := admissionregistrationv1apply.ValidatingAdmissionPolicy(policy.Name). 155 WithStatus(admissionregistrationv1apply.ValidatingAdmissionPolicyStatus(). 156 WithObservedGeneration(policy.Generation). 157 WithTypeChecking(admissionregistrationv1apply.TypeChecking(). 158 WithExpressionWarnings(warningsConfig...))) 159 _, err := c.policyClient.ApplyStatus(ctx, applyConfig, metav1.ApplyOptions{FieldManager: ControllerName, Force: true}) 160 return err 161 }