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  }