sigs.k8s.io/cluster-api-provider-aws@v1.5.5/api/v1beta1/awsmachinetemplate_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 "github.com/google/go-cmp/cmp" 21 apierrors "k8s.io/apimachinery/pkg/api/errors" 22 "k8s.io/apimachinery/pkg/runtime" 23 "k8s.io/apimachinery/pkg/util/validation/field" 24 ctrl "sigs.k8s.io/controller-runtime" 25 "sigs.k8s.io/controller-runtime/pkg/webhook" 26 27 "sigs.k8s.io/cluster-api-provider-aws/feature" 28 ) 29 30 func (r *AWSMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { 31 return ctrl.NewWebhookManagedBy(mgr). 32 For(r). 33 Complete() 34 } 35 36 // +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-awsmachinetemplate,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=awsmachinetemplates,versions=v1beta1,name=validation.awsmachinetemplate.infrastructure.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 37 38 var ( 39 _ webhook.Validator = &AWSMachineTemplate{} 40 ) 41 42 func (r *AWSMachineTemplate) validateRootVolume() field.ErrorList { 43 var allErrs field.ErrorList 44 45 spec := r.Spec.Template.Spec 46 if spec.RootVolume == nil { 47 return allErrs 48 } 49 50 if VolumeTypesProvisioned.Has(string(spec.RootVolume.Type)) && spec.RootVolume.IOPS == 0 { 51 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.rootVolume.iops"), "iops required if type is 'io1' or 'io2'")) 52 } 53 54 if spec.RootVolume.Throughput != nil { 55 if spec.RootVolume.Type != VolumeTypeGP3 { 56 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.rootVolume.throughput"), "throughput is valid only for type 'gp3'")) 57 } 58 if *spec.RootVolume.Throughput < 0 { 59 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.rootVolume.throughput"), "throughput must be nonnegative")) 60 } 61 } 62 63 if spec.RootVolume.DeviceName != "" { 64 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.template.spec.rootVolume.deviceName"), "root volume shouldn't have device name")) 65 } 66 67 return allErrs 68 } 69 70 func (r *AWSMachineTemplate) validateNonRootVolumes() field.ErrorList { 71 var allErrs field.ErrorList 72 73 spec := r.Spec.Template.Spec 74 75 for _, volume := range spec.NonRootVolumes { 76 if VolumeTypesProvisioned.Has(string(volume.Type)) && volume.IOPS == 0 { 77 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.nonRootVolumes.iops"), "iops required if type is 'io1' or 'io2'")) 78 } 79 80 if volume.Throughput != nil { 81 if volume.Type != VolumeTypeGP3 { 82 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.nonRootVolumes.throughput"), "throughput is valid only for type 'gp3'")) 83 } 84 if *volume.Throughput < 0 { 85 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.nonRootVolumes.throughput"), "throughput must be nonnegative")) 86 } 87 } 88 89 if volume.DeviceName == "" { 90 allErrs = append(allErrs, field.Required(field.NewPath("spec.template.spec.nonRootVolumes.deviceName"), "non root volume should have device name")) 91 } 92 } 93 94 return allErrs 95 } 96 97 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. 98 func (r *AWSMachineTemplate) ValidateCreate() error { 99 var allErrs field.ErrorList 100 spec := r.Spec.Template.Spec 101 102 if spec.CloudInit.SecretPrefix != "" { 103 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "template", "spec", "cloudInit", "secretPrefix"), "cannot be set in templates")) 104 } 105 106 if spec.CloudInit.SecretCount != 0 { 107 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "cloudInit", "secretCount"), "cannot be set in templates")) 108 } 109 110 if spec.ProviderID != nil { 111 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "template", "spec", "providerID"), "cannot be set in templates")) 112 } 113 114 allErrs = append(allErrs, r.validateRootVolume()...) 115 allErrs = append(allErrs, r.validateNonRootVolumes()...) 116 117 // Feature gate is not enabled but ignition is enabled then send a forbidden error. 118 if !feature.Gates.Enabled(feature.BootstrapFormatIgnition) && spec.Ignition != nil { 119 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "ignition"), 120 "can be set only if the BootstrapFormatIgnition feature gate is enabled")) 121 } 122 123 cloudInitConfigured := spec.CloudInit.SecureSecretsBackend != "" || spec.CloudInit.InsecureSkipSecretsManager 124 if cloudInitConfigured && spec.Ignition != nil { 125 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "template", "spec", "cloudInit"), 126 "cannot be set if spec.template.spec.ignition is set")) 127 } 128 129 return aggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, allErrs) 130 } 131 132 // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. 133 func (r *AWSMachineTemplate) ValidateUpdate(old runtime.Object) error { 134 oldAWSMachineTemplate := old.(*AWSMachineTemplate) 135 136 // Allow setting of cloudInit.secureSecretsBackend to "secrets-manager" only to handle v1beta1 upgrade 137 if oldAWSMachineTemplate.Spec.Template.Spec.CloudInit.SecureSecretsBackend == "" && r.Spec.Template.Spec.CloudInit.SecureSecretsBackend == SecretBackendSecretsManager { 138 r.Spec.Template.Spec.CloudInit.SecureSecretsBackend = "" 139 } 140 141 if !cmp.Equal(r.Spec, oldAWSMachineTemplate.Spec) { 142 return apierrors.NewBadRequest("AWSMachineTemplate.Spec is immutable") 143 } 144 145 return nil 146 } 147 148 // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. 149 func (r *AWSMachineTemplate) ValidateDelete() error { 150 return nil 151 }