sigs.k8s.io/cluster-api-provider-aws@v1.5.5/exp/api/v1beta1/awsmachinepool_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 "time" 21 22 apierrors "k8s.io/apimachinery/pkg/api/errors" 23 "k8s.io/apimachinery/pkg/runtime" 24 "k8s.io/apimachinery/pkg/util/validation/field" 25 ctrl "sigs.k8s.io/controller-runtime" 26 logf "sigs.k8s.io/controller-runtime/pkg/log" 27 "sigs.k8s.io/controller-runtime/pkg/webhook" 28 29 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 30 ) 31 32 var log = logf.Log.WithName("awsmachinepool-resource") 33 34 // SetupWebhookWithManager will setup the webhooks for the AWSMachinePool. 35 func (r *AWSMachinePool) SetupWebhookWithManager(mgr ctrl.Manager) error { 36 return ctrl.NewWebhookManagedBy(mgr). 37 For(r). 38 Complete() 39 } 40 41 // +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-awsmachinepool,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=awsmachinepools,versions=v1beta1,name=validation.awsmachinepool.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 42 // +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-awsmachinepool,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=awsmachinepools,versions=v1beta1,name=default.awsmachinepool.infrastructure.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1beta1 43 44 var _ webhook.Defaulter = &AWSMachinePool{} 45 var _ webhook.Validator = &AWSMachinePool{} 46 47 func (r *AWSMachinePool) validateDefaultCoolDown() field.ErrorList { 48 var allErrs field.ErrorList 49 50 if int(r.Spec.DefaultCoolDown.Duration.Seconds()) < 0 { 51 allErrs = append(allErrs, field.Required(field.NewPath("spec.DefaultCoolDown"), "DefaultCoolDown must be greater than zero")) 52 } 53 54 return allErrs 55 } 56 57 func (r *AWSMachinePool) validateRootVolume() field.ErrorList { 58 var allErrs field.ErrorList 59 60 if r.Spec.AWSLaunchTemplate.RootVolume == nil { 61 return allErrs 62 } 63 64 if v1beta1.VolumeTypesProvisioned.Has(string(r.Spec.AWSLaunchTemplate.RootVolume.Type)) && r.Spec.AWSLaunchTemplate.RootVolume.IOPS == 0 { 65 allErrs = append(allErrs, field.Required(field.NewPath("spec.awsLaunchTemplate.rootVolume.iops"), "iops required if type is 'io1' or 'io2'")) 66 } 67 68 if r.Spec.AWSLaunchTemplate.RootVolume.Throughput != nil { 69 if r.Spec.AWSLaunchTemplate.RootVolume.Type != v1beta1.VolumeTypeGP3 { 70 allErrs = append(allErrs, field.Required(field.NewPath("spec.awsLaunchTemplate.rootVolume.throughput"), "throughput is valid only for type 'gp3'")) 71 } 72 if *r.Spec.AWSLaunchTemplate.RootVolume.Throughput < 0 { 73 allErrs = append(allErrs, field.Required(field.NewPath("spec.awsLaunchTemplate.rootVolume.throughput"), "throughput must be nonnegative")) 74 } 75 } 76 77 if r.Spec.AWSLaunchTemplate.RootVolume.DeviceName != "" { 78 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.awsLaunchTemplate.rootVolume.deviceName"), "root volume shouldn't have device name")) 79 } 80 81 return allErrs 82 } 83 84 func (r *AWSMachinePool) validateSubnets() field.ErrorList { 85 var allErrs field.ErrorList 86 87 if r.Spec.Subnets == nil { 88 return allErrs 89 } 90 91 for _, subnet := range r.Spec.Subnets { 92 if subnet.ARN != nil { 93 log.Info("ARN field is deprecated and is no operation function.") 94 } 95 if subnet.ID != nil && subnet.Filters != nil { 96 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.subnets.filters"), "providing either subnet ID or filter is supported, should not provide both")) 97 break 98 } 99 } 100 101 return allErrs 102 } 103 104 func (r *AWSMachinePool) validateAdditionalSecurityGroups() field.ErrorList { 105 var allErrs field.ErrorList 106 for _, sg := range r.Spec.AWSLaunchTemplate.AdditionalSecurityGroups { 107 if sg.ID != nil && sg.Filters != nil { 108 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.awsLaunchTemplate.AdditionalSecurityGroups"), "either ID or filters should be used")) 109 } 110 if sg.ARN != nil { 111 log.Info("ARN field is deprecated and is no operation function.") 112 } 113 } 114 return allErrs 115 } 116 117 // ValidateCreate will do any extra validation when creating a AWSMachinePool. 118 func (r *AWSMachinePool) ValidateCreate() error { 119 log.Info("AWSMachinePool validate create", "name", r.Name) 120 121 var allErrs field.ErrorList 122 123 allErrs = append(allErrs, r.validateDefaultCoolDown()...) 124 allErrs = append(allErrs, r.validateRootVolume()...) 125 allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...) 126 allErrs = append(allErrs, r.validateSubnets()...) 127 allErrs = append(allErrs, r.validateAdditionalSecurityGroups()...) 128 129 if len(allErrs) == 0 { 130 return nil 131 } 132 133 return apierrors.NewInvalid( 134 r.GroupVersionKind().GroupKind(), 135 r.Name, 136 allErrs, 137 ) 138 } 139 140 // ValidateUpdate will do any extra validation when updating a AWSMachinePool. 141 func (r *AWSMachinePool) ValidateUpdate(old runtime.Object) error { 142 var allErrs field.ErrorList 143 144 allErrs = append(allErrs, r.validateDefaultCoolDown()...) 145 allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...) 146 allErrs = append(allErrs, r.validateSubnets()...) 147 allErrs = append(allErrs, r.validateAdditionalSecurityGroups()...) 148 149 if len(allErrs) == 0 { 150 return nil 151 } 152 153 return apierrors.NewInvalid( 154 r.GroupVersionKind().GroupKind(), 155 r.Name, 156 allErrs, 157 ) 158 } 159 160 // ValidateDelete allows you to add any extra validation when deleting. 161 func (r *AWSMachinePool) ValidateDelete() error { 162 return nil 163 } 164 165 // Default will set default values for the AWSMachinePool. 166 func (r *AWSMachinePool) Default() { 167 if int(r.Spec.DefaultCoolDown.Duration.Seconds()) == 0 { 168 log.Info("DefaultCoolDown is zero, setting 300 seconds as default") 169 r.Spec.DefaultCoolDown.Duration = 300 * time.Second 170 } 171 }