sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azuremachine_webhook.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 v1beta1 18 19 import ( 20 "context" 21 "reflect" 22 23 apierrors "k8s.io/apimachinery/pkg/api/errors" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/util/validation/field" 26 webhookutils "sigs.k8s.io/cluster-api-provider-azure/util/webhook" 27 ctrl "sigs.k8s.io/controller-runtime" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 30 ) 31 32 // SetupAzureMachineWebhookWithManager sets up and registers the webhook with the manager. 33 func SetupAzureMachineWebhookWithManager(mgr ctrl.Manager) error { 34 mw := &azureMachineWebhook{Client: mgr.GetClient()} 35 return ctrl.NewWebhookManagedBy(mgr). 36 For(&AzureMachine{}). 37 WithDefaulter(mw). 38 WithValidator(mw). 39 Complete() 40 } 41 42 // +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-azuremachine,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=azuremachines,versions=v1beta1,name=validation.azuremachine.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 43 // +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-azuremachine,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=azuremachines,versions=v1beta1,name=default.azuremachine.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 44 45 // azureMachineWebhook implements a validating and defaulting webhook for AzureMachines. 46 type azureMachineWebhook struct { 47 Client client.Client 48 } 49 50 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. 51 func (mw *azureMachineWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { 52 m, ok := obj.(*AzureMachine) 53 if !ok { 54 return nil, apierrors.NewBadRequest("expected an AzureMachine resource") 55 } 56 spec := m.Spec 57 58 allErrs := ValidateAzureMachineSpec(spec) 59 60 roleAssignmentName := "" 61 if spec.SystemAssignedIdentityRole != nil { 62 roleAssignmentName = spec.SystemAssignedIdentityRole.Name 63 } 64 65 if errs := ValidateSystemAssignedIdentity(spec.Identity, "", roleAssignmentName, field.NewPath("roleAssignmentName")); len(errs) > 0 { 66 allErrs = append(allErrs, errs...) 67 } 68 69 if len(allErrs) == 0 { 70 return nil, nil 71 } 72 73 return nil, apierrors.NewInvalid(GroupVersion.WithKind(AzureMachineKind).GroupKind(), m.Name, allErrs) 74 } 75 76 // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. 77 func (mw *azureMachineWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { 78 var allErrs field.ErrorList 79 old, ok := oldObj.(*AzureMachine) 80 if !ok { 81 return nil, apierrors.NewBadRequest("expected an AzureMachine resource") 82 } 83 m, ok := newObj.(*AzureMachine) 84 if !ok { 85 return nil, apierrors.NewBadRequest("expected an AzureMachine resource") 86 } 87 88 if err := webhookutils.ValidateImmutable( 89 field.NewPath("Spec", "Image"), 90 old.Spec.Image, 91 m.Spec.Image); err != nil { 92 allErrs = append(allErrs, err) 93 } 94 95 if err := webhookutils.ValidateImmutable( 96 field.NewPath("Spec", "Identity"), 97 old.Spec.Identity, 98 m.Spec.Identity); err != nil { 99 allErrs = append(allErrs, err) 100 } 101 102 if err := webhookutils.ValidateImmutable( 103 field.NewPath("Spec", "SystemAssignedIdentityRole"), 104 old.Spec.SystemAssignedIdentityRole, 105 m.Spec.SystemAssignedIdentityRole); err != nil { 106 allErrs = append(allErrs, err) 107 } 108 109 if err := webhookutils.ValidateImmutable( 110 field.NewPath("Spec", "UserAssignedIdentities"), 111 old.Spec.UserAssignedIdentities, 112 m.Spec.UserAssignedIdentities); err != nil { 113 allErrs = append(allErrs, err) 114 } 115 116 if err := webhookutils.ValidateImmutable( 117 field.NewPath("Spec", "RoleAssignmentName"), 118 old.Spec.RoleAssignmentName, 119 m.Spec.RoleAssignmentName); err != nil { 120 allErrs = append(allErrs, err) 121 } 122 123 if err := webhookutils.ValidateImmutable( 124 field.NewPath("Spec", "OSDisk"), 125 old.Spec.OSDisk, 126 m.Spec.OSDisk); err != nil { 127 allErrs = append(allErrs, err) 128 } 129 130 if err := webhookutils.ValidateImmutable( 131 field.NewPath("Spec", "DataDisks"), 132 old.Spec.DataDisks, 133 m.Spec.DataDisks); err != nil { 134 allErrs = append(allErrs, err) 135 } 136 137 if err := webhookutils.ValidateImmutable( 138 field.NewPath("Spec", "SSHPublicKey"), 139 old.Spec.SSHPublicKey, 140 m.Spec.SSHPublicKey); err != nil { 141 allErrs = append(allErrs, err) 142 } 143 144 if err := webhookutils.ValidateImmutable( 145 field.NewPath("Spec", "AllocatePublicIP"), 146 old.Spec.AllocatePublicIP, 147 m.Spec.AllocatePublicIP); err != nil { 148 allErrs = append(allErrs, err) 149 } 150 151 if err := webhookutils.ValidateImmutable( 152 field.NewPath("Spec", "EnableIPForwarding"), 153 old.Spec.EnableIPForwarding, 154 m.Spec.EnableIPForwarding); err != nil { 155 allErrs = append(allErrs, err) 156 } 157 158 // Spec.AcceleratedNetworking can only be reset to nil and no other changes apart from that 159 // is accepted if the field is set. 160 // Ref issue #3518 161 if err := webhookutils.ValidateZeroTransition( 162 field.NewPath("Spec", "AcceleratedNetworking"), 163 old.Spec.AcceleratedNetworking, 164 m.Spec.AcceleratedNetworking); err != nil { 165 allErrs = append(allErrs, err) 166 } 167 168 if err := webhookutils.ValidateImmutable( 169 field.NewPath("Spec", "SpotVMOptions"), 170 old.Spec.SpotVMOptions, 171 m.Spec.SpotVMOptions); err != nil { 172 allErrs = append(allErrs, err) 173 } 174 175 if err := webhookutils.ValidateImmutable( 176 field.NewPath("Spec", "SecurityProfile"), 177 old.Spec.SecurityProfile, 178 m.Spec.SecurityProfile); err != nil { 179 allErrs = append(allErrs, err) 180 } 181 182 if old.Spec.Diagnostics != nil { 183 if err := webhookutils.ValidateImmutable( 184 field.NewPath("Spec", "Diagnostics"), 185 old.Spec.Diagnostics, 186 m.Spec.Diagnostics); err != nil { 187 allErrs = append(allErrs, err) 188 } 189 } 190 191 if !reflect.DeepEqual(m.Spec.NetworkInterfaces, old.Spec.NetworkInterfaces) { 192 // The defaulting webhook may have migrated values from the old SubnetName field to the new NetworkInterfaces format. 193 old.Spec.SetNetworkInterfacesDefaults() 194 195 // The reconciler will populate the SubnetName on the first interface if the user left it blank. 196 if old.Spec.NetworkInterfaces[0].SubnetName == "" && m.Spec.NetworkInterfaces[0].SubnetName != "" { 197 old.Spec.NetworkInterfaces[0].SubnetName = m.Spec.NetworkInterfaces[0].SubnetName 198 } 199 200 // Enforce immutability for all other changes to NetworkInterfaces. 201 if !reflect.DeepEqual(m.Spec.NetworkInterfaces, old.Spec.NetworkInterfaces) { 202 allErrs = append(allErrs, 203 field.Invalid(field.NewPath("spec", "networkInterfaces"), 204 m.Spec.NetworkInterfaces, "field is immutable"), 205 ) 206 } 207 } 208 209 if len(allErrs) == 0 { 210 return nil, nil 211 } 212 return nil, apierrors.NewInvalid(GroupVersion.WithKind(AzureMachineKind).GroupKind(), m.Name, allErrs) 213 } 214 215 // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. 216 func (mw *azureMachineWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { 217 return nil, nil 218 } 219 220 // Default implements webhook.Defaulter so a webhook will be registered for the type. 221 func (mw *azureMachineWebhook) Default(ctx context.Context, obj runtime.Object) error { 222 m, ok := obj.(*AzureMachine) 223 if !ok { 224 return apierrors.NewBadRequest("expected an AzureMachine resource") 225 } 226 return m.SetDefaults(mw.Client) 227 }