open-cluster-management.io/governance-policy-propagator@v0.13.0/api/v1/policy_webhook.go (about)

     1  // Copyright (c) 2021 Red Hat, Inc.
     2  // Copyright Contributors to the Open Cluster Management project
     3  
     4  package v1
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"unicode/utf8"
    11  
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"k8s.io/apimachinery/pkg/runtime"
    14  	ctrl "sigs.k8s.io/controller-runtime"
    15  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    16  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    17  	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
    18  )
    19  
    20  var (
    21  	// log is for logging in this package.
    22  	policylog = logf.Log.WithName("policy-validating-webhook")
    23  	errName   = errors.New("the combined length of the policy namespace and name " +
    24  		"cannot exceed 62 characters")
    25  	errRemediation = errors.New("RemediationAction field of the policy and " +
    26  		"policy template cannot both be unset")
    27  )
    28  
    29  func (r *Policy) SetupWebhookWithManager(mgr ctrl.Manager) error {
    30  	return ctrl.NewWebhookManagedBy(mgr).
    31  		For(r).
    32  		Complete()
    33  }
    34  
    35  //+kubebuilder:webhook:path=/validate-policy-open-cluster-management-io-v1-policy,mutating=false,failurePolicy=Ignore,sideEffects=None,groups=policy.open-cluster-management.io,resources=policies,verbs=create,versions=v1,name=policy.open-cluster-management.io.webhook,admissionReviewVersions=v1
    36  
    37  var _ webhook.Validator = &Policy{}
    38  
    39  // ValidateCreate implements webhook.Validator so a webhook will be registered for the type
    40  func (r *Policy) ValidateCreate() (admission.Warnings, error) {
    41  	log := policylog.WithValues("policyName", r.Name, "policyNamespace", r.Namespace)
    42  	log.V(1).Info("Validate policy creation request")
    43  
    44  	err := r.validateName()
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	err = r.validateRemediationAction()
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return nil, nil
    55  }
    56  
    57  // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
    58  func (r *Policy) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
    59  	log := policylog.WithValues("policyName", r.Name, "policyNamespace", r.Namespace)
    60  	log.V(1).Info("Validate policy update request")
    61  
    62  	err := r.validateRemediationAction()
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	return nil, nil
    68  }
    69  
    70  // ValidateDelete implements webhook.Validator so a webhook will be registered for the type
    71  func (r *Policy) ValidateDelete() (admission.Warnings, error) {
    72  	return nil, nil
    73  }
    74  
    75  // validate the policy name and namespace length
    76  func (r *Policy) validateName() error {
    77  	log := policylog.WithValues("policyName", r.Name, "policyNamespace", r.Namespace)
    78  	log.V(1).Info("Validating the policy name through a validating webhook")
    79  
    80  	// replicated policies don't need pass this validation
    81  	if _, ok := r.GetLabels()["policy.open-cluster-management.io/root-policy"]; ok {
    82  		return nil
    83  	}
    84  
    85  	// 1 character for "."
    86  	if (utf8.RuneCountInString(r.Name) + utf8.RuneCountInString(r.Namespace)) > 62 {
    87  		log.Info(fmt.Sprintf("Invalid policy name/namespace: %s", errName.Error()))
    88  
    89  		return errName
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // validate the remediationAction field of the root policy and its policy templates
    96  func (r *Policy) validateRemediationAction() error {
    97  	log := policylog.WithValues("policyName", r.Name, "policyNamespace", r.Namespace)
    98  	log.V(1).Info("Validating the Policy and ConfigurationPolicy remediationAction through a validating webhook")
    99  
   100  	if r.Spec.RemediationAction != "" {
   101  		return nil
   102  	}
   103  
   104  	plcTemplates := r.Spec.PolicyTemplates
   105  
   106  	for _, obj := range plcTemplates {
   107  		objUnstruct := &unstructured.Unstructured{}
   108  		_ = json.Unmarshal(obj.ObjectDefinition.Raw, objUnstruct)
   109  
   110  		if objUnstruct.GroupVersionKind().Kind == "ConfigurationPolicy" {
   111  			_, found, _ := unstructured.NestedString(objUnstruct.Object, "spec", "remediationAction")
   112  			if !found {
   113  				log.Info(fmt.Sprintf("Invalid remediationAction configuration: %s", errRemediation.Error()))
   114  
   115  				return errRemediation
   116  			}
   117  		}
   118  	}
   119  
   120  	return nil
   121  }