Repository: vmware-tanzu/tanzu-framework (language: go)

Issue:
Webhook for AntreaConfig and CalicoConfig
- AntreaConfig immutable fields are TLSCipherSuites, DefaultMTU, TrafficEncapMode, NoSNAT, DisableUDPTunnelOffload
- CalicoConfig immutable fields are namespace and VethMTU
 


  


Discussion / hints:

Fix patch:
diff --git a/apis/cni/v1alpha1/antreaconfig_webhook.go b/apis/cni/v1alpha1/antreaconfig_webhook.go
new file mode 100644
index 0000000000..94466ff76b
--- /dev/null
+++ b/apis/cni/v1alpha1/antreaconfig_webhook.go
@@ -0,0 +1,115 @@
+// Copyright 2022 VMware, Inc. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package v1alpha1
+
+import (
+	"fmt"
+	"reflect"
+
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/util/validation/field"
+	ctrl "sigs.k8s.io/controller-runtime"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+)
+
+// log is for logging in this package.
+var antreaconfiglog = logf.Log.WithName("antreaconfig-resource")
+
+func (r *AntreaConfig) SetupWebhookWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewWebhookManagedBy(mgr).
+		For(r).
+		Complete()
+}
+
+var _ webhook.Validator = &AntreaConfig{}
+
+// +kubebuilder:webhook:verbs=create;update,path=/validate-cni-tanzu-vmware-com-v1alpha1-antreaconfig,mutating=false,failurePolicy=fail,groups=cni.tanzu.vmware.com,resources=antreaconfigs,versions=v1alpha1,name=vantreaconfig.kb.io,admissionReviewVersions=v1,sideEffects=None
+
+// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
+func (r *AntreaConfig) ValidateCreate() error {
+	antreaconfiglog.Info("validate create", "name", r.Name)
+
+	var allErrs field.ErrorList
+
+	if !r.Spec.Antrea.AntreaConfigDataValue.FeatureGates.AntreaProxy &&
+		r.Spec.Antrea.AntreaConfigDataValue.FeatureGates.EndpointSlice {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "antrea", "config", "featureGates", "EndpointSlice"),
+				r.Spec.Antrea.AntreaConfigDataValue.FeatureGates.EndpointSlice,
+				"field cannot be enabled if AntreaProxy is disabled"),
+		)
+	}
+
+	if r.Spec.Antrea.AntreaConfigDataValue.NoSNAT &&
+		(r.Spec.Antrea.AntreaConfigDataValue.TrafficEncapMode == "encap" ||
+			r.Spec.Antrea.AntreaConfigDataValue.TrafficEncapMode == "hybrid") {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "antrea", "config", "noSNAT"),
+				r.Spec.Antrea.AntreaConfigDataValue.NoSNAT,
+				"field must be disabled for encap and hybrid mode"),
+		)
+	}
+
+	if len(allErrs) == 0 {
+		return nil
+	}
+	return apierrors.NewInvalid(
+		schema.GroupKind{Group: "cni.tanzu.vmware.com", Kind: "AntreaConfig"}, r.Name, allErrs)
+}
+
+// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
+func (r *AntreaConfig) ValidateUpdate(old runtime.Object) error {
+	antreaconfiglog.Info("validate update", "name", r.Name)
+
+	oldObj, ok := old.(*AntreaConfig)
+	if !ok {
+		return apierrors.NewBadRequest(fmt.Sprintf("Expected an AntreaConfig but got a %T", oldObj))
+	}
+
+	var allErrs field.ErrorList
+
+	// Check for changes to immutable fields and return errors
+	if !reflect.DeepEqual(r.Spec.Antrea.AntreaConfigDataValue.NoSNAT,
+		oldObj.Spec.Antrea.AntreaConfigDataValue.NoSNAT) {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "antrea", "config", "noSNAT"),
+				r.Spec.Antrea.AntreaConfigDataValue.NoSNAT, "field is immutable"),
+		)
+	}
+	if !reflect.DeepEqual(r.Spec.Antrea.AntreaConfigDataValue.DisableUDPTunnelOffload,
+		oldObj.Spec.Antrea.AntreaConfigDataValue.DisableUDPTunnelOffload) {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "antrea", "config", "disableUdpTunnelOffload"),
+				r.Spec.Antrea.AntreaConfigDataValue.DisableUDPTunnelOffload, "field is immutable"),
+		)
+	}
+	if !reflect.DeepEqual(r.Spec.Antrea.AntreaConfigDataValue.TLSCipherSuites,
+		oldObj.Spec.Antrea.AntreaConfigDataValue.TLSCipherSuites) {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "antrea", "config", "tlsCipherSuites"),
+				r.Spec.Antrea.AntreaConfigDataValue.TLSCipherSuites, "field is immutable"),
+		)
+	}
+	if !reflect.DeepEqual(r.Spec.Antrea.AntreaConfigDataValue.TrafficEncapMode,
+		oldObj.Spec.Antrea.AntreaConfigDataValue.TrafficEncapMode) {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "antrea", "config", "trafficEncapMode"),
+				r.Spec.Antrea.AntreaConfigDataValue.TrafficEncapMode, "field is immutable"),
+		)
+	}
+	if len(allErrs) == 0 {
+		return nil
+	}
+	return apierrors.NewInvalid(
+		schema.GroupKind{Group: "cni.tanzu.vmware.com", Kind: "AntreaConfig"}, r.Name, allErrs)
+}
+
+// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
+func (r *AntreaConfig) ValidateDelete() error {
+	// No validation required for AntreaConfig deletion
+	return nil
+}
diff --git a/apis/cni/v1alpha1/calicoconfig_webhook.go b/apis/cni/v1alpha1/calicoconfig_webhook.go
new file mode 100644
index 0000000000..7f54c6e25b
--- /dev/null
+++ b/apis/cni/v1alpha1/calicoconfig_webhook.go
@@ -0,0 +1,66 @@
+// Copyright 2022 VMware, Inc. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package v1alpha1
+
+import (
+	"fmt"
+	"reflect"
+
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/util/validation/field"
+	ctrl "sigs.k8s.io/controller-runtime"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+)
+
+// log is for logging in this package.
+var calicoconfiglog = logf.Log.WithName("calicoconfig-resource")
+
+func (r *CalicoConfig) SetupWebhookWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewWebhookManagedBy(mgr).
+		For(r).
+		Complete()
+}
+
+var _ webhook.Validator = &CalicoConfig{}
+
+// +kubebuilder:webhook:verbs=create;update,path=/validate-cni-tanzu-vmware-com-v1alpha1-calicoconfig,mutating=false,failurePolicy=fail,groups=cni.tanzu.vmware.com,resources=calicoconfigs,versions=v1alpha1,name=vcalicoconfig.kb.io,admissionReviewVersions=v1,sideEffects=None
+
+// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
+func (r *CalicoConfig) ValidateCreate() error {
+	return nil
+}
+
+// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
+func (r *CalicoConfig) ValidateUpdate(old runtime.Object) error {
+	calicoconfiglog.Info("validate update", "name", r.Name)
+
+	oldObj, ok := old.(*CalicoConfig)
+	if !ok {
+		return apierrors.NewBadRequest(fmt.Sprintf("Expected an CalicoConfig but got a %T", oldObj))
+	}
+
+	var allErrs field.ErrorList
+
+	// Check for changes to immutable fields and return errors
+	if !reflect.DeepEqual(r.Spec.Namespace, oldObj.Spec.Namespace) {
+		allErrs = append(allErrs,
+			field.Invalid(field.NewPath("spec", "namespace"),
+				r.Spec.Namespace, "field is immutable"),
+		)
+	}
+	if len(allErrs) == 0 {
+		return nil
+	}
+	return apierrors.NewInvalid(
+		schema.GroupKind{Group: "cni.tanzu.vmware.com", Kind: "CalicoConfig"}, r.Name, allErrs)
+}
+
+// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
+func (r *CalicoConfig) ValidateDelete() error {
+	// No validation required for CalicoConfig deletion
+	return nil
+}
diff --git a/apis/cni/v1alpha1/zz_generated.deepcopy.go b/apis/cni/v1alpha1/zz_generated.deepcopy.go
index f9f79ee19c..64a19b9dee 100644
--- a/apis/cni/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/cni/v1alpha1/zz_generated.deepcopy.go
@@ -9,7 +9,7 @@
 package v1alpha1
 
 import (
-	runtime "k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime"
 )
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.


Test patch:
diff --git a/addons/controllers/antreaconfig_controller_test.go b/addons/controllers/antreaconfig_controller_test.go
index d12e0e086c..df5a2b2ac1 100644
--- a/addons/controllers/antreaconfig_controller_test.go
+++ b/addons/controllers/antreaconfig_controller_test.go
@@ -19,35 +19,71 @@ import (
 	cniv1alpha1 "github.com/vmware-tanzu/tanzu-framework/apis/cni/v1alpha1"
 )
 
-var _ = Describe("AntreaConfig Reconciler", func() {
+var _ = Describe("AntreaConfig Reconciler and Webhooks", func() {
 	var (
 		configCRName            string
 		clusterResourceFilePath string
+		err                     error
+		f                       *os.File
+	)
+
+	const (
+		antreaManifestsTestFile1 = "testdata/antrea-test-1.yaml"
+		antreaTestCluster1       = "test-cluster-4"
 	)
 
 	JustBeforeEach(func() {
+		// Create the admission webhooks
+		f, err = os.Open(cniWebhookManifestFile)
+		Expect(err).ToNot(HaveOccurred())
+		err = testutil.CreateResources(f, cfg, dynamicClient)
+		Expect(err).ToNot(HaveOccurred())
+		f.Close()
+
+		// set up the certificates and webhook before creating any objects
+		By("Creating and installing new certificates for Antrea Admission Webhooks")
+
+		webhookCertDetails := testutil.WebhookCertificatesDetails{
+			CertPath:           certPath,
+			KeyPath:            keyPath,
+			WebhookScrtName:    webhookScrtName,
+			AddonNamespace:     addonNamespace,
+			WebhookServiceName: webhookServiceName,
+			LabelSelector:      cniWebhookLabel,
+		}
+		err = testutil.SetupWebhookCertificates(ctx, k8sClient, k8sConfig, &webhookCertDetails)
+		Expect(err).ToNot(HaveOccurred())
+
 		// create cluster resources
 		By("Creating a cluster and a AntreaConfig")
-		f, err := os.Open(clusterResourceFilePath)
+		f, err = os.Open(clusterResourceFilePath)
 		Expect(err).ToNot(HaveOccurred())
-		defer f.Close()
 		err = testutil.CreateResources(f, cfg, dynamicClient)
 		Expect(err).ToNot(HaveOccurred())
+		f.Close()
 	})
 
 	AfterEach(func() {
 		By("Deleting cluster and AntreaConfig")
-		f, err := os.Open(clusterResourceFilePath)
+		f, err = os.Open(clusterResourceFilePath)
 		Expect(err).ToNot(HaveOccurred())
-		defer f.Close()
-		Expect(testutil.DeleteResources(f, cfg, dynamicClient, true)).To(Succeed())
+		err = testutil.DeleteResources(f, cfg, dynamicClient, true)
+		Expect(err).ToNot(HaveOccurred())
+		f.Close()
+
+		By("Deleting the Admission Webhook configuration for Antrea")
+		f, err = os.Open(cniWebhookManifestFile)
+		Expect(err).ToNot(HaveOccurred())
+		err = testutil.DeleteResources(f, cfg, dynamicClient, true)
+		Expect(err).ToNot(HaveOccurred())
+		f.Close()
 	})
 
 	Context("Reconcile AntreaConfig for management cluster", func() {
 
 		BeforeEach(func() {
-			configCRName = "test-cluster-4"
-			clusterResourceFilePath = "testdata/antrea-test-1.yaml"
+			configCRName = antreaTestCluster1
+			clusterResourceFilePath = antreaManifestsTestFile1
 		})
 
 		It("Should reconcile AntreaConfig and create data value secret on management cluster", func() {
@@ -154,4 +190,26 @@ var _ = Describe("AntreaConfig Reconciler", func() {
 
 	})
 
+	Context("Mutating webhooks for AntreaConfig", func() {
+
+		BeforeEach(func() {
+			configCRName = antreaTestCluster1
+			clusterResourceFilePath = antreaManifestsTestFile1
+		})
+
+		It("Should fail mutating webhooks for immutable field for AntreaConfig", func() {
+
+			key := client.ObjectKey{
+				Namespace: "default",
+				Name:      configCRName,
+			}
+			config := &cniv1alpha1.AntreaConfig{}
+			Expect(k8sClient.Get(ctx, key, config)).To(Succeed())
+
+			By("Trying to update the immutable TrafficEncapMode field in Antrea Spec")
+			config.Spec.Antrea.AntreaConfigDataValue.TrafficEncapMode = "noEncap"
+			Expect(k8sClient.Update(ctx, config)).ToNot(Succeed())
+		})
+	})
+
 })
diff --git a/addons/controllers/calicoconfig_controller_test.go b/addons/controllers/calicoconfig_controller_test.go
index 2a122169e8..b49f2a7e1a 100644
--- a/addons/controllers/calicoconfig_controller_test.go
+++ b/addons/controllers/calicoconfig_controller_test.go
@@ -21,34 +21,62 @@ import (
 
 const testCluster = "test-cluster-calico"
 
-var _ = Describe("CalicoConfig Reconciler", func() {
+var _ = Describe("CalicoConfig Reconciler and Webhooks", func() {
 	var (
-		clusterName             string
-		clusterResourceFilePath string
+		clusterName string
 	)
 
+	const (
+		clusterResourceFilePath = "testdata/test-calico.yaml"
+	)
 	JustBeforeEach(func() {
+		// Create the admission webhooks
+		f, err := os.Open(cniWebhookManifestFile)
+		Expect(err).ToNot(HaveOccurred())
+		err = testutil.CreateResources(f, cfg, dynamicClient)
+		Expect(err).ToNot(HaveOccurred())
+		f.Close()
+
+		// set up the certificates and webhook before creating any objects
+		By("Creating and installing new certificates for Calico Admission Webhooks")
+		webhookCertDetails := testutil.WebhookCertificatesDetails{
+			CertPath:           certPath,
+			KeyPath:            keyPath,
+			WebhookScrtName:    webhookScrtName,
+			AddonNamespace:     addonNamespace,
+			WebhookServiceName: webhookServiceName,
+			LabelSelector:      cniWebhookLabel,
+		}
+		err = testutil.SetupWebhookCertificates(ctx, k8sClient, k8sConfig, &webhookCertDetails)
+		Expect(err).ToNot(HaveOccurred())
+
 		By("Creating cluster and CalicoConfig resources")
-		f, err := os.Open(clusterResourceFilePath)
+		f, err = os.Open(clusterResourceFilePath)
 		Expect(err).ToNot(HaveOccurred())
-		defer f.Close()
 		err = testutil.CreateResources(f, cfg, dynamicClient)
 		Expect(err).ToNot(HaveOccurred())
+		f.Close()
 	})
 
 	AfterEach(func() {
 		By("Deleting cluster and CalicoConfig resources")
 		f, err := os.Open(clusterResourceFilePath)
 		Expect(err).ToNot(HaveOccurred())
-		defer f.Close()
 		err = testutil.DeleteResources(f, cfg, dynamicClient, true)
 		Expect(err).ToNot(HaveOccurred())
+		f.Close()
+
+		By("Deleting the Admission Webhook configuration for Calico")
+		f, err = os.Open(cniWebhookManifestFile)
+		Expect(err).ToNot(HaveOccurred())
+		err = testutil.DeleteResources(f, cfg, dynamicClient, true)
+		Expect(err).ToNot(HaveOccurred())
+		f.Close()
 	})
 
 	Context("reconcile CalicoConfig for management cluster", func() {
 		BeforeEach(func() {
 			clusterName = testCluster
-			clusterResourceFilePath = "testdata/test-calico.yaml"
 		})
 
 		It("Should reconcile CalicoConfig and create data values secret for CalicoConfig on management cluster", func() {
@@ -71,7 +99,7 @@ var _ = Describe("CalicoConfig Reconciler", func() {
 					return false
 				}
 
-				//check spec values
+				// check spec values
 				Expect(config.Spec.Namespace).Should(Equal("kube-system"))
 				Expect(config.Spec.Calico.Config.VethMTU).Should(Equal(int64(0)))
 
@@ -119,4 +147,23 @@ var _ = Describe("CalicoConfig Reconciler", func() {
 			}, waitTimeout, pollingInterval).Should(BeTrue())
 		})
 	})
+
+	Context("Calico Admission Webhooks", func() {
+		BeforeEach(func() {
+			clusterName = testCluster
+		})
+
+		It("Should fail mutating webhooks for immutable fields for CalicoConfig", func() {
+			key := client.ObjectKey{
+				Namespace: "default",
+				Name:      testCluster,
+			}
+			config := &cniv1alpha1.CalicoConfig{}
+			Expect(k8sClient.Get(ctx, key, config)).To(Succeed())
+
+			By("Trying to update the immutable Namespace field in Calico Spec")
+			config.Spec.Namespace = "default"
+			Expect(k8sClient.Update(ctx, config)).ToNot(Succeed())
+		})
+	})
 })
diff --git a/addons/controllers/certificates_test.go b/addons/controllers/certificates_test.go
index 74cf3b0620..512deeb7b8 100644
--- a/addons/controllers/certificates_test.go
+++ b/addons/controllers/certificates_test.go
@@ -4,13 +4,13 @@
 package controllers
 
 import (
-	adminregv1 "k8s.io/api/admissionregistration/v1"
-	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/apimachinery/pkg/selection"
+	"os"
+
 	cert2 "k8s.io/client-go/util/cert"
 	"k8s.io/client-go/util/keyutil"
 	"knative.dev/pkg/webhook/certificates/resources"
-	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/vmware-tanzu/tanzu-framework/addons/testutil"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
@@ -19,6 +19,15 @@ import (
 )
 
 var _ = Describe("Webhook", func() {
+	Context("Webhook Manifests", func() {
+		It("Should create webhook manifests for tests", func() {
+			f, err := os.Open("testdata/test-webhook-manifests.yaml")
+			Expect(err).ToNot(HaveOccurred())
+			defer f.Close()
+			err = testutil.CreateResources(f, cfg, dynamicClient)
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
 
 	Context("server's certificate and key", func() {
 		It("should be generated and written to the webhook server CertDir", func() {
@@ -41,44 +50,17 @@ var _ = Describe("Webhook", func() {
 	Context("Mutating and Validating Configurations", func() {
 		It("should be updated with CA bundle ", func() {
 			var err error
-
-			labelMatch, _ := labels.NewRequirement("webhook-cert", selection.Equals, []string{"self-managed"})
-			labelSelector := labels.NewSelector()
-			labelSelector = labelSelector.Add(*labelMatch)
-
-			orgvwcfgs := &adminregv1.ValidatingWebhookConfigurationList{}
-			err = k8sClient.List(ctx, orgvwcfgs, &client.ListOptions{LabelSelector: labelSelector})
-			Expect(err).ToNot(HaveOccurred())
-			Expect(orgvwcfgs.Items).ToNot(BeEmpty())
-
-			orgmwcfgs := &adminregv1.MutatingWebhookConfigurationList{}
-			err = k8sClient.List(ctx, orgmwcfgs, &client.ListOptions{LabelSelector: labelSelector})
-			Expect(err).ToNot(HaveOccurred())
-			Expect(orgmwcfgs.Items).ToNot(BeEmpty())
-
-			secret, err := webhooks.InstallNewCertificates(ctx, k8sConfig, certPath, keyPath, webhookScrtName, addonNamespace, webhookServiceName, "webhook-cert=self-managed")
-			Expect(err).ToNot(HaveOccurred())
-
-			vwcfgs := &adminregv1.ValidatingWebhookConfigurationList{}
-			err = k8sClient.List(ctx, vwcfgs, &client.ListOptions{LabelSelector: labelSelector})
-			Expect(err).ToNot(HaveOccurred())
-			Expect(vwcfgs.Items).ToNot(BeEmpty())
-			for _, wcfg := range vwcfgs.Items {
-				for _, whook := range wcfg.Webhooks {
-					Expect(whook.ClientConfig.CABundle).To(Equal(secret.Data[resources.CACert]))
-				}
+			webhookCertDetails := testutil.WebhookCertificatesDetails{
+				CertPath:           certPath,
+				KeyPath:            keyPath,
+				WebhookScrtName:    webhookScrtName,
+				AddonNamespace:     addonNamespace,
+				WebhookServiceName: webhookServiceName,
+				LabelSelector:      "self-managed",
 			}
 
-			mwcfgs := &adminregv1.MutatingWebhookConfigurationList{}
-			err = k8sClient.List(ctx, mwcfgs, &client.ListOptions{LabelSelector: labelSelector})
+			err = testutil.SetupWebhookCertificates(ctx, k8sClient, k8sConfig, &webhookCertDetails)
 			Expect(err).ToNot(HaveOccurred())
-			Expect(mwcfgs.Items).ToNot(BeEmpty())
-			for _, wcfg := range mwcfgs.Items {
-				for _, whook := range wcfg.Webhooks {
-					Expect(whook.ClientConfig.CABundle).To(Equal(secret.Data[resources.CACert]))
-				}
-			}
-
 		})
 		It("should fail fail update when  label selector is not provided", func() {
 			_, err := webhooks.InstallNewCertificates(ctx, k8sConfig, certPath, keyPath, webhookScrtName, addonNamespace, webhookServiceName, "")
@@ -86,4 +68,13 @@ var _ = Describe("Webhook", func() {
 		})
 	})
 
+	Context("Delete webhook Manifests", func() {
+		It("Should delete webhook manifests after tests", func() {
+			f, err := os.Open("testdata/test-webhook-manifests.yaml")
+			Expect(err).ToNot(HaveOccurred())
+			defer f.Close()
+			err = testutil.DeleteResources(f, cfg, dynamicClient, true)
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
 })
diff --git a/addons/controllers/suite_test.go b/addons/controllers/suite_test.go
index 351f4ae230..25dddc1c17 100644
--- a/addons/controllers/suite_test.go
+++ b/addons/controllers/suite_test.go
@@ -43,11 +43,11 @@ import (
 	"github.com/vmware-tanzu/tanzu-framework/addons/pkg/constants"
 	"github.com/vmware-tanzu/tanzu-framework/addons/pkg/crdwait"
 	testutil "github.com/vmware-tanzu/tanzu-framework/addons/testutil"
-
 	cniv1alpha1 "github.com/vmware-tanzu/tanzu-framework/apis/cni/v1alpha1"
 	cpiv1alpha1 "github.com/vmware-tanzu/tanzu-framework/apis/cpi/v1alpha1"
 	runtanzuv1alpha1 "github.com/vmware-tanzu/tanzu-framework/apis/run/v1alpha1"
 	runtanzuv1alpha3 "github.com/vmware-tanzu/tanzu-framework/apis/run/v1alpha3"
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/webhooks"
 )
 
 // These tests use Ginkgo (BDD-style Go testing framework). Refer to
@@ -66,6 +66,8 @@ const (
 	corePackageRepoName     = "core"
 	webhookServiceName      = "webhook-service"
 	webhookScrtName         = "webhook-tls"
+	cniWebhookLabel         = "cni-webhook"
+	cniWebhookManifestFile  = "testdata/test-antrea-calico-webhook-manifests.yaml"
 )
 
 var (
@@ -75,6 +77,7 @@ var (
 	testEnv       *envtest.Environment
 	ctx           = ctrl.SetupSignalHandler()
 	scheme        = runtime.NewScheme()
+	mgr           manager.Manager
 	dynamicClient dynamic.Interface
 	cancel        context.CancelFunc
 	certPath      string
@@ -166,16 +169,17 @@ var _ = BeforeSuite(func(done Done) {
 
 	tmpDir, err := os.MkdirTemp("/tmp", "webhooktest")
 	Expect(err).ToNot(HaveOccurred())
-	certPath = path.Join(tmpDir, "tls.cert")
+	certPath = path.Join(tmpDir, "tls.crt")
 	keyPath = path.Join(tmpDir, "tls.key")
 
 	options := manager.Options{
 		Scheme:             scheme,
 		MetricsBindAddress: "0",
+		Host:               "127.0.0.1",
 		Port:               9443,
 		CertDir:            tmpDir,
 	}
-	mgr, err := ctrl.NewManager(testEnv.Config, options)
+	mgr, err = ctrl.NewManager(testEnv.Config, options)
 	Expect(err).ToNot(HaveOccurred())
 	k8sConfig = mgr.GetConfig()
 
@@ -267,11 +271,13 @@ var _ = BeforeSuite(func(done Done) {
 	ns = &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "tkg-system"}}
 	Expect(k8sClient.Create(context.TODO(), ns)).To(Succeed())
 
-	// Create the admission webhooks
-	f, err := os.Open("testdata/test-webhook-manifests.yaml")
+	_, err = webhooks.InstallNewCertificates(ctx, k8sConfig, certPath, keyPath, webhookScrtName, addonNamespace, webhookServiceName, "webhook-cert=self-managed")
+	Expect(err).ToNot(HaveOccurred())
+
+	// Set up the webhooks in the manager
+	err = (&cniv1alpha1.AntreaConfig{}).SetupWebhookWithManager(mgr)
 	Expect(err).ToNot(HaveOccurred())
-	defer f.Close()
-	err = testutil.CreateResources(f, cfg, dynamicClient)
+	err = (&cniv1alpha1.CalicoConfig{}).SetupWebhookWithManager(mgr)
 	Expect(err).ToNot(HaveOccurred())
 
 	go func() {
diff --git a/addons/controllers/testdata/antrea-test-1.yaml b/addons/controllers/testdata/antrea-test-1.yaml
index a5d55f553c..880659ec05 100644
--- a/addons/controllers/testdata/antrea-test-1.yaml
+++ b/addons/controllers/testdata/antrea-test-1.yaml
@@ -13,7 +13,7 @@ spec:
   infrastructureRef:
     apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
     kind: DockerCluster
-    name: test-cluster-1
+    name: test-cluster-4
 ---
 apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
 kind: DockerCluster
diff --git a/addons/controllers/testdata/test-antrea-calico-webhook-manifests.yaml b/addons/controllers/testdata/test-antrea-calico-webhook-manifests.yaml
new file mode 100644
index 0000000000..61889bd9e5
--- /dev/null
+++ b/addons/controllers/testdata/test-antrea-calico-webhook-manifests.yaml
@@ -0,0 +1,68 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: webhook-service
+  namespace: tkg-system
+spec:
+  type: ExternalName
+  externalName: 127.0.0.1
+---
+apiVersion: admissionregistration.k8s.io/v1
+kind: ValidatingWebhookConfiguration
+metadata:
+  creationTimestamp: null
+  labels:
+    webhook-cert: cni-webhook
+  name: validating-webhook-configuration
+webhooks:
+  - admissionReviewVersions:
+      - v1
+      - v1beta1
+    clientConfig:
+      #placeholder for cabundle. To be modifed by patch or programatically.
+      caBundle: Cg==
+      service:
+        name: webhook-service
+        namespace: tkg-system
+        path: /validate-cni-tanzu-vmware-com-v1alpha1-antreaconfig
+        port: 9443
+    failurePolicy: Fail
+    name: vantreaconfig.kb.io
+    rules:
+      - apiGroups:
+          - cni.tanzu.vmware.com
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - antreaconfigs
+    sideEffects: None
+    timeoutSeconds: 30
+  - admissionReviewVersions:
+      - v1
+      - v1beta1
+    clientConfig:
+      #placeholder for cabundle. To be modifed by patch or programatically.
+      caBundle: Cg==
+      service:
+        name: webhook-service
+        namespace: tkg-system
+        path: /validate-cni-tanzu-vmware-com-v1alpha1-calicoconfig
+        port: 9443
+    failurePolicy: Fail
+    name: vcalicoconfig.kb.io
+    rules:
+      - apiGroups:
+          - cni.tanzu.vmware.com
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - calicoconfigs
+    sideEffects: None
+    timeoutSeconds: 30
diff --git a/addons/controllers/testdata/test-webhook-manifests.yaml b/addons/controllers/testdata/test-webhook-manifests.yaml
index bb3fbb5ff0..cf5ca45173 100644
--- a/addons/controllers/testdata/test-webhook-manifests.yaml
+++ b/addons/controllers/testdata/test-webhook-manifests.yaml
@@ -5,12 +5,8 @@ metadata:
   name: webhook-service
   namespace: tkg-system
 spec:
-  ports:
-    - port: 443
-      protocol: TCP
-      targetPort: 9443
-  selector:
-    control-plane: controller-manager
+  type: ExternalName
+  externalName: 127.0.0.1
 ---
 apiVersion: admissionregistration.k8s.io/v1
 kind: MutatingWebhookConfiguration
@@ -30,18 +26,19 @@ webhooks:
         name: webhook-service
         namespace: tkg-system
         path: /mutate-cni-tanzu-vmware-com-v1alpha1-antreaconfig
+        port: 9443
     failurePolicy: Fail
     name: mantreaconfig.kb.io
-#    rules:
-#      - apiGroups:
-#          - cni.tanzu.vmware.com
-#        apiVersions:
-#          - v1alpha1
-#        operations:
-#          - CREATE
-#          - UPDATE
-#        resources:
-#          - antreaconfigs
+    rules:
+      - apiGroups:
+          - cni.tanzu.vmware.com
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - antreaconfigs
     sideEffects: None
     timeoutSeconds: 30
   - admissionReviewVersions:
@@ -54,18 +51,19 @@ webhooks:
         name: webhook-service
         namespace: tkg-system
         path: /mutate-cni-tanzu-vmware-com-v1alpha1-calicoconfig
+        port: 9443
     failurePolicy: Fail
     name: mcalicoconfig.kb.io
-#    rules:
-#      - apiGroups:
-#          - cni.tanzu.vmware.com
-#        apiVersions:
-#          - v1alpha1
-#        operations:
-#          - CREATE
-#          - UPDATE
-#        resources:
-#          - calicoconfigs
+    rules:
+      - apiGroups:
+          - cni.tanzu.vmware.com
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - calicoconfigs
     sideEffects: None
     timeoutSeconds: 30
 ---
@@ -87,18 +85,19 @@ webhooks:
         name: webhook-service
         namespace: tkg-system
         path: /validate-cni-tanzu-vmware-com-v1alpha1-antreaconfig
+        port: 9443
     failurePolicy: Fail
     name: vantreaconfig.kb.io
-#    rules:
-#      - apiGroups:
-#          - cni.tanzu.vmware.com
-#        apiVersions:
-#          - v1alpha1
-#        operations:
-#          - CREATE
-#          - UPDATE
-#        resources:
-#          - antreaconfigs
+    rules:
+      - apiGroups:
+          - cni.tanzu.vmware.com
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - antreaconfigs
     sideEffects: None
     timeoutSeconds: 30
   - admissionReviewVersions:
@@ -111,17 +110,18 @@ webhooks:
         name: webhook-service
         namespace: tkg-system
         path: /validate-cni-tanzu-vmware-com-v1alpha1-calicoconfig
+        port: 9443
     failurePolicy: Fail
     name: vcalicoconfig.kb.io
-#    rules:
-#      - apiGroups:
-#          - cni.tanzu.vmware.com
-#        apiVersions:
-#          - v1alpha1
-#        operations:
-#          - CREATE
-#          - UPDATE
-#        resources:
-#          - calicoconfigs
+    rules:
+      - apiGroups:
+          - cni.tanzu.vmware.com
+        apiVersions:
+          - v1alpha1
+        operations:
+          - CREATE
+          - UPDATE
+        resources:
+          - calicoconfigs
     sideEffects: None
     timeoutSeconds: 30
diff --git a/addons/testutil/test_helpers.go b/addons/testutil/test_helpers.go
index fa8ddde824..a5e7b3c325 100644
--- a/addons/testutil/test_helpers.go
+++ b/addons/testutil/test_helpers.go
@@ -16,24 +16,30 @@ import (
 	"time"
 
 	"github.com/onsi/ginkgo"
+	adminregv1 "k8s.io/api/admissionregistration/v1"
 	v1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
+	"k8s.io/apimachinery/pkg/selection"
 	"k8s.io/apimachinery/pkg/util/wait"
 	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
 	"k8s.io/client-go/dynamic"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+	"knative.dev/pkg/webhook/certificates/resources"
 	clusterapiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util/secret"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
+
+	"github.com/vmware-tanzu/tanzu-framework/pkg/v1/webhooks"
 )
 
 // CreateResources using unstructured objects from a yaml/json file provided by decoder
@@ -252,3 +258,60 @@ func GetExternalCRDPaths(externalDeps map[string][]string) ([]string, error) {
 	logf.Log.Info("external CRD paths", "crdPaths", crdPaths)
 	return crdPaths, nil
 }
+
+type WebhookCertificatesDetails struct {
+	CertPath           string
+	KeyPath            string
+	WebhookScrtName    string
+	AddonNamespace     string
+	WebhookServiceName string
+	LabelSelector      string
+}
+
+func SetupWebhookCertificates(ctx context.Context, k8sClient client.Client, k8sConfig *rest.Config, certDetails *WebhookCertificatesDetails) error {
+	labelMatch, _ := labels.NewRequirement("webhook-cert", selection.Equals, []string{certDetails.LabelSelector})
+	labelSelector := labels.NewSelector()
+	labelSelector = labelSelector.Add(*labelMatch)
+
+	scrt, err := webhooks.InstallNewCertificates(ctx, k8sConfig, certDetails.CertPath, certDetails.KeyPath,
+		certDetails.WebhookScrtName, certDetails.AddonNamespace, certDetails.WebhookServiceName, "webhook-cert="+certDetails.LabelSelector)
+	if err != nil {
+		return err
+	}
+	vwcfgs := &adminregv1.ValidatingWebhookConfigurationList{}
+	err = k8sClient.List(ctx, vwcfgs, &client.ListOptions{LabelSelector: labelSelector})
+	if err != nil {
+		return err
+	}
+	if len(vwcfgs.Items) == 0 {
+		return fmt.Errorf("validating Webhook Configuration List is empty")
+	}
+	for i := range vwcfgs.Items {
+		wcfg := vwcfgs.Items[i]
+		for j := range wcfg.Webhooks {
+			whook := wcfg.Webhooks[j]
+			if !bytes.Equal(whook.ClientConfig.CABundle, scrt.Data[resources.CACert]) {
+				return fmt.Errorf("validating Webhook CA Bundlle is not updated correctly")
+			}
+		}
+	}
+
+	mwcfgs := &adminregv1.MutatingWebhookConfigurationList{}
+	err = k8sClient.List(ctx, mwcfgs, &client.ListOptions{LabelSelector: labelSelector})
+	if err != nil {
+		return err
+	}
+	if len(vwcfgs.Items) == 0 {
+		return fmt.Errorf("mutating Webhook Configuration List is empty")
+	}
+	for i := range mwcfgs.Items {
+		wcfg := mwcfgs.Items[i]
+		for j := range wcfg.Webhooks {
+			whook := wcfg.Webhooks[j]
+			if !bytes.Equal(whook.ClientConfig.CABundle, scrt.Data[resources.CACert]) {
+				return fmt.Errorf("malidating Webhook CA Bundlle is not updated correctly")
+			}
+		}
+	}
+	return nil
+}