sigs.k8s.io/cluster-api@v1.7.1/internal/test/envtest/webhooks.go (about)

     1  /*
     2  Copyright 2021 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 envtest
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path"
    23  	"path/filepath"
    24  	goruntime "runtime"
    25  	"time"
    26  
    27  	admissionv1 "k8s.io/api/admissionregistration/v1"
    28  	"k8s.io/client-go/kubernetes/scheme"
    29  	"k8s.io/klog/v2"
    30  	"sigs.k8s.io/controller-runtime/pkg/envtest"
    31  
    32  	utilyaml "sigs.k8s.io/cluster-api/util/yaml"
    33  )
    34  
    35  const (
    36  	mutatingWebhookKind   = "MutatingWebhookConfiguration"
    37  	validatingWebhookKind = "ValidatingWebhookConfiguration"
    38  	mutatingwebhook       = "mutating-webhook-configuration"
    39  	validatingwebhook     = "validating-webhook-configuration"
    40  )
    41  
    42  func initWebhookInstallOptions() envtest.WebhookInstallOptions {
    43  	validatingWebhooks := []*admissionv1.ValidatingWebhookConfiguration{}
    44  	mutatingWebhooks := []*admissionv1.MutatingWebhookConfiguration{}
    45  
    46  	// Get the root of the current file to use in CRD paths.
    47  	_, filename, _, _ := goruntime.Caller(0) //nolint:dogsled
    48  	root := path.Join(path.Dir(filename), "..", "..", "..")
    49  	configyamlFile, err := os.ReadFile(filepath.Join(root, "config", "webhook", "manifests.yaml")) //nolint:gosec
    50  	if err != nil {
    51  		klog.Fatalf("Failed to read core webhook configuration file: %v ", err)
    52  	}
    53  	if err != nil {
    54  		klog.Fatalf("failed to parse yaml")
    55  	}
    56  	// append the webhook with suffix to avoid clashing webhooks. repeated for every webhook
    57  	mutatingWebhooks, validatingWebhooks, err = appendWebhookConfiguration(mutatingWebhooks, validatingWebhooks, configyamlFile, "config")
    58  	if err != nil {
    59  		klog.Fatalf("Failed to append core controller webhook config: %v", err)
    60  	}
    61  
    62  	bootstrapyamlFile, err := os.ReadFile(filepath.Join(root, "bootstrap", "kubeadm", "config", "webhook", "manifests.yaml")) //nolint:gosec
    63  	if err != nil {
    64  		klog.Fatalf("Failed to get bootstrap yaml file: %v", err)
    65  	}
    66  	mutatingWebhooks, validatingWebhooks, err = appendWebhookConfiguration(mutatingWebhooks, validatingWebhooks, bootstrapyamlFile, "bootstrap")
    67  
    68  	if err != nil {
    69  		klog.Fatalf("Failed to append bootstrap controller webhook config: %v", err)
    70  	}
    71  	controlplaneyamlFile, err := os.ReadFile(filepath.Join(root, "controlplane", "kubeadm", "config", "webhook", "manifests.yaml")) //nolint:Gosec
    72  	if err != nil {
    73  		klog.Fatalf(" Failed to get controlplane yaml file err: %v", err)
    74  	}
    75  	mutatingWebhooks, validatingWebhooks, err = appendWebhookConfiguration(mutatingWebhooks, validatingWebhooks, controlplaneyamlFile, "cp")
    76  	if err != nil {
    77  		klog.Fatalf("Failed to append controlplane controller webhook config: %v", err)
    78  	}
    79  	return envtest.WebhookInstallOptions{
    80  		MaxTime:                      20 * time.Second,
    81  		PollInterval:                 time.Second,
    82  		ValidatingWebhooks:           validatingWebhooks,
    83  		MutatingWebhooks:             mutatingWebhooks,
    84  		LocalServingHostExternalName: os.Getenv("CAPI_WEBHOOK_HOSTNAME"),
    85  	}
    86  }
    87  
    88  // Mutate the name of each webhook, because kubebuilder generates the same name for all controllers.
    89  // In normal usage, kustomize will prefix the controller name, which we have to do manually here.
    90  func appendWebhookConfiguration(mutatingWebhooks []*admissionv1.MutatingWebhookConfiguration, validatingWebhooks []*admissionv1.ValidatingWebhookConfiguration, configyamlFile []byte, tag string) ([]*admissionv1.MutatingWebhookConfiguration, []*admissionv1.ValidatingWebhookConfiguration, error) {
    91  	objs, err := utilyaml.ToUnstructured(configyamlFile)
    92  	if err != nil {
    93  		klog.Fatalf("failed to parse yaml")
    94  	}
    95  	// look for resources of kind MutatingWebhookConfiguration
    96  	for i := range objs {
    97  		o := objs[i]
    98  		if o.GetKind() == mutatingWebhookKind {
    99  			// update the name in metadata
   100  			if o.GetName() == mutatingwebhook {
   101  				o.SetName(fmt.Sprintf("%s-%s", mutatingwebhook, tag))
   102  
   103  				webhook := &admissionv1.MutatingWebhookConfiguration{}
   104  				if err := scheme.Scheme.Convert(&o, webhook, nil); err != nil {
   105  					klog.Fatalf("failed to convert MutatingWebhookConfiguration %s", o.GetName())
   106  				}
   107  
   108  				mutatingWebhooks = append(mutatingWebhooks, webhook)
   109  			}
   110  		}
   111  		if o.GetKind() == validatingWebhookKind {
   112  			// update the name in metadata
   113  			if o.GetName() == validatingwebhook {
   114  				o.SetName(fmt.Sprintf("%s-%s", validatingwebhook, tag))
   115  
   116  				webhook := &admissionv1.ValidatingWebhookConfiguration{}
   117  				if err := scheme.Scheme.Convert(&o, webhook, nil); err != nil {
   118  					klog.Fatalf("failed to convert ValidatingWebhookConfiguration %s", o.GetName())
   119  				}
   120  
   121  				validatingWebhooks = append(validatingWebhooks, webhook)
   122  			}
   123  		}
   124  	}
   125  	return mutatingWebhooks, validatingWebhooks, err
   126  }