github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasadmission/admission.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasadmission
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  
    10  	"github.com/juju/errors"
    11  	admission "k8s.io/api/admissionregistration/v1"
    12  	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  
    14  	k8sconstants "github.com/juju/juju/caas/kubernetes/provider/constants"
    15  	k8sutils "github.com/juju/juju/caas/kubernetes/provider/utils"
    16  	"github.com/juju/juju/pki"
    17  )
    18  
    19  // AdmissionCreator represents a creator of mutating webhooks that is context aware of the
    20  // current controller.
    21  type AdmissionCreator interface {
    22  	EnsureMutatingWebhookConfiguration() (func(), error)
    23  }
    24  
    25  // AdmissionCreatorFunc is the func type of AdmissionCreator.
    26  type AdmissionCreatorFunc func() (func(), error)
    27  
    28  const (
    29  	// Component describes a sub zone to use on the juju tld for unique resource
    30  	// ids. For example using this component "admission" with "juju.io" would
    31  	// yield admission.juju.io
    32  	Component = "admission"
    33  
    34  	// we still accept v1beta1 AdmissionReview only.
    35  	reviewVersionV1beta1 = "v1beta1"
    36  )
    37  
    38  var (
    39  	anyMatch = []string{"*"}
    40  )
    41  
    42  // EnsureMutatingWebhookConfiguration implements AdmissionCreator interface for
    43  // func type.
    44  func (a AdmissionCreatorFunc) EnsureMutatingWebhookConfiguration() (func(), error) {
    45  	return a()
    46  }
    47  
    48  // NewAdmissionCreator instantiates a new AdmissionCreator for the supplied
    49  // context arguments.
    50  func NewAdmissionCreator(
    51  	authority pki.Authority,
    52  	namespace, modelName string,
    53  	legacyLabels bool,
    54  	ensureConfig func(*admission.MutatingWebhookConfiguration) (func(), error),
    55  	service *admission.ServiceReference) (AdmissionCreator, error) {
    56  
    57  	caPemBuffer := bytes.Buffer{}
    58  	if err := pki.CertificateToPemWriter(&caPemBuffer, map[string]string{},
    59  		authority.Certificate()); err != nil {
    60  		return nil, errors.Trace(err)
    61  	}
    62  
    63  	// TODO change to fail
    64  	failurePolicy := admission.Ignore
    65  	matchPolicy := admission.Equivalent
    66  	ruleScope := admission.AllScopes
    67  	sideEffects := admission.SideEffectClassNone
    68  
    69  	// MutatingWebhook Obj
    70  	obj := admission.MutatingWebhookConfiguration{
    71  		ObjectMeta: meta.ObjectMeta{
    72  			Labels:    k8sutils.LabelsForModel(modelName, legacyLabels),
    73  			Name:      fmt.Sprintf("juju-model-admission-%s", namespace),
    74  			Namespace: namespace,
    75  		},
    76  		Webhooks: []admission.MutatingWebhook{
    77  			{
    78  				SideEffects: &sideEffects,
    79  				ClientConfig: admission.WebhookClientConfig{
    80  					CABundle: caPemBuffer.Bytes(),
    81  					Service:  service,
    82  				},
    83  				AdmissionReviewVersions: []string{reviewVersionV1beta1},
    84  				FailurePolicy:           &failurePolicy,
    85  				MatchPolicy:             &matchPolicy,
    86  				Name:                    k8sutils.MakeK8sDomain(Component),
    87  				NamespaceSelector: &meta.LabelSelector{
    88  					MatchLabels: k8sutils.LabelsForModel(modelName, legacyLabels),
    89  				},
    90  				ObjectSelector: &meta.LabelSelector{
    91  					MatchExpressions: []meta.LabelSelectorRequirement{
    92  						{
    93  							Key:      k8sconstants.LabelJujuModelOperatorDisableWebhook,
    94  							Operator: meta.LabelSelectorOpDoesNotExist,
    95  						},
    96  					},
    97  				},
    98  				Rules: []admission.RuleWithOperations{
    99  					{
   100  						Operations: []admission.OperationType{
   101  							admission.Create,
   102  							admission.Update,
   103  						},
   104  						Rule: admission.Rule{
   105  							APIGroups:   anyMatch,
   106  							APIVersions: anyMatch,
   107  							Resources:   anyMatch,
   108  							Scope:       &ruleScope,
   109  						},
   110  					},
   111  				},
   112  			},
   113  		},
   114  	}
   115  
   116  	return AdmissionCreatorFunc(func() (func(), error) {
   117  		leafGroup := fmt.Sprintf("k8sadmission-%s", modelName)
   118  		_, err := authority.LeafRequestForGroup(leafGroup).
   119  			AddDNSNames(fmt.Sprintf("%s.%s.svc", service.Name, service.Namespace)).
   120  			Commit()
   121  		if err != nil {
   122  			return nil, errors.Trace(err)
   123  		}
   124  
   125  		configCleanup, err := ensureConfig(&obj)
   126  		if err != nil {
   127  			return nil, errors.Trace(err)
   128  		}
   129  
   130  		return func() {
   131  			configCleanup()
   132  		}, nil
   133  	}), nil
   134  }