diff --git a/exp/api/v1beta1/azuremanagedcluster_webhook.go b/exp/api/v1beta1/azuremanagedcluster_webhook.go index 40e901468572..13251b26c878 100644 --- a/exp/api/v1beta1/azuremanagedcluster_webhook.go +++ b/exp/api/v1beta1/azuremanagedcluster_webhook.go @@ -26,6 +26,7 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/azure" "sigs.k8s.io/cluster-api-provider-azure/feature" "sigs.k8s.io/cluster-api-provider-azure/util/maps" + webhookutils "sigs.k8s.io/cluster-api-provider-azure/util/webhook" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/webhook" ) @@ -70,6 +71,24 @@ func (r *AzureManagedCluster) ValidateUpdate(oldRaw runtime.Object) error { fmt.Sprintf("annotations with '%s' prefix are immutable", azure.CustomHeaderPrefix))) } + if old.Spec.ControlPlaneEndpoint.Host != "" { + if err := webhookutils.ValidateImmutable( + field.NewPath("Spec", "ControlPlaneEndpoint", "Host"), + old.Spec.ControlPlaneEndpoint.Host, + r.Spec.ControlPlaneEndpoint.Host); err != nil { + allErrs = append(allErrs, err) + } + } + + if old.Spec.ControlPlaneEndpoint.Port != 0 { + if err := webhookutils.ValidateImmutable( + field.NewPath("Spec", "ControlPlaneEndpoint", "Port"), + old.Spec.ControlPlaneEndpoint.Port, + r.Spec.ControlPlaneEndpoint.Port); err != nil { + allErrs = append(allErrs, err) + } + } + if len(allErrs) != 0 { return apierrors.NewInvalid(GroupVersion.WithKind("AzureManagedCluster").GroupKind(), r.Name, allErrs) } diff --git a/exp/api/v1beta1/azuremanagedcluster_webhook_test.go b/exp/api/v1beta1/azuremanagedcluster_webhook_test.go index bd5f203fbb21..f4b060c7ad62 100644 --- a/exp/api/v1beta1/azuremanagedcluster_webhook_test.go +++ b/exp/api/v1beta1/azuremanagedcluster_webhook_test.go @@ -23,6 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilfeature "k8s.io/component-base/featuregate/testing" "sigs.k8s.io/cluster-api-provider-azure/feature" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func TestAzureManagedCluster_ValidateUpdate(t *testing.T) { @@ -119,6 +120,69 @@ func TestAzureManagedCluster_ValidateUpdate(t *testing.T) { }, wantErr: false, }, + { + name: "ControlPlaneEndpoint.Port is immutable", + oldAMC: &AzureManagedCluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: AzureManagedClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 443, + }, + }, + }, + amc: &AzureManagedCluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: AzureManagedClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 444, + }, + }, + }, + wantErr: true, + }, + { + name: "ControlPlaneEndpoint.Host is immutable", + oldAMC: &AzureManagedCluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: AzureManagedClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 443, + }, + }, + }, + amc: &AzureManagedCluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: AzureManagedClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "this-is-not-allowed", + Port: 443, + }, + }, + }, + wantErr: true, + }, + { + name: "ControlPlaneEndpoint update from zero values are allowed", + oldAMC: &AzureManagedCluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: AzureManagedClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{}, + }, + }, + amc: &AzureManagedCluster{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: AzureManagedClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 443, + }, + }, + }, + wantErr: false, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { diff --git a/exp/api/v1beta1/azuremanagedcontrolplane_webhook.go b/exp/api/v1beta1/azuremanagedcontrolplane_webhook.go index 73cae853b3c3..451df1c7845d 100644 --- a/exp/api/v1beta1/azuremanagedcontrolplane_webhook.go +++ b/exp/api/v1beta1/azuremanagedcontrolplane_webhook.go @@ -193,6 +193,24 @@ func (m *AzureManagedControlPlane) ValidateUpdate(oldRaw runtime.Object, client } } + if old.Spec.ControlPlaneEndpoint.Host != "" { + if err := webhookutils.ValidateImmutable( + field.NewPath("Spec", "ControlPlaneEndpoint", "Host"), + old.Spec.ControlPlaneEndpoint.Host, + m.Spec.ControlPlaneEndpoint.Host); err != nil { + allErrs = append(allErrs, err) + } + } + + if old.Spec.ControlPlaneEndpoint.Port != 0 { + if err := webhookutils.ValidateImmutable( + field.NewPath("Spec", "ControlPlaneEndpoint", "Port"), + old.Spec.ControlPlaneEndpoint.Port, + m.Spec.ControlPlaneEndpoint.Port); err != nil { + allErrs = append(allErrs, err) + } + } + if errs := m.validateVirtualNetworkUpdate(old); len(errs) > 0 { allErrs = append(allErrs, errs...) } diff --git a/exp/api/v1beta1/azuremanagedcontrolplane_webhook_test.go b/exp/api/v1beta1/azuremanagedcontrolplane_webhook_test.go index f4aff9f4b34e..fbfd9906722e 100644 --- a/exp/api/v1beta1/azuremanagedcontrolplane_webhook_test.go +++ b/exp/api/v1beta1/azuremanagedcontrolplane_webhook_test.go @@ -25,6 +25,7 @@ import ( utilfeature "k8s.io/component-base/featuregate/testing" "k8s.io/utils/pointer" "sigs.k8s.io/cluster-api-provider-azure/feature" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func TestDefaultingWebhook(t *testing.T) { @@ -903,6 +904,81 @@ func TestAzureManagedControlPlane_ValidateUpdate(t *testing.T) { }, wantErr: false, }, + { + name: "AzureManagedControlPlane ControlPlaneEndpoint.Port is mutable", + oldAMCP: &AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Spec: AzureManagedControlPlaneSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 443, + }, + }, + }, + amcp: &AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Spec: AzureManagedControlPlaneSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 444, + }, + }, + }, + wantErr: true, + }, + { + name: "AzureManagedControlPlane ControlPlaneEndpoint.Host is mutable", + oldAMCP: &AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Spec: AzureManagedControlPlaneSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 443, + }, + }, + }, + amcp: &AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Spec: AzureManagedControlPlaneSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "this-is-not-allowed", + Port: 443, + }, + }, + }, + wantErr: true, + }, + { + name: "ControlPlaneEndpoint update from zero values are allowed", + oldAMCP: &AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Spec: AzureManagedControlPlaneSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{}, + }, + }, + amcp: &AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Spec: AzureManagedControlPlaneSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "aks-8622-h4h26c44.hcp.eastus.azmk8s.io", + Port: 443, + }, + }, + }, + wantErr: true, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) {