sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/webhooks/kubeadmcontrolplanetemplate.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 webhooks 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/util/validation/field" 27 ctrl "sigs.k8s.io/controller-runtime" 28 "sigs.k8s.io/controller-runtime/pkg/webhook" 29 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 30 31 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 32 controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" 33 "sigs.k8s.io/cluster-api/feature" 34 ) 35 36 const kubeadmControlPlaneTemplateImmutableMsg = "KubeadmControlPlaneTemplate spec.template.spec field is immutable. Please create new resource instead." 37 38 func (webhook *KubeadmControlPlaneTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { 39 return ctrl.NewWebhookManagedBy(mgr). 40 For(&controlplanev1.KubeadmControlPlaneTemplate{}). 41 WithDefaulter(webhook). 42 WithValidator(webhook). 43 Complete() 44 } 45 46 // +kubebuilder:webhook:verbs=create;update,path=/validate-controlplane-cluster-x-k8s-io-v1beta1-kubeadmcontrolplanetemplate,mutating=false,failurePolicy=fail,groups=controlplane.cluster.x-k8s.io,resources=kubeadmcontrolplanetemplates,versions=v1beta1,name=validation.kubeadmcontrolplanetemplate.controlplane.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 47 // +kubebuilder:webhook:verbs=create;update,path=/mutate-controlplane-cluster-x-k8s-io-v1beta1-kubeadmcontrolplanetemplate,mutating=true,failurePolicy=fail,groups=controlplane.cluster.x-k8s.io,resources=kubeadmcontrolplanetemplates,versions=v1beta1,name=default.kubeadmcontrolplanetemplate.controlplane.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 48 49 // KubeadmControlPlaneTemplate implements a validation and defaulting webhook for KubeadmControlPlaneTemplate. 50 type KubeadmControlPlaneTemplate struct{} 51 52 var _ webhook.CustomValidator = &KubeadmControlPlaneTemplate{} 53 var _ webhook.CustomDefaulter = &KubeadmControlPlaneTemplate{} 54 55 // Default implements webhook.Defaulter so a webhook will be registered for the type. 56 func (webhook *KubeadmControlPlaneTemplate) Default(_ context.Context, obj runtime.Object) error { 57 k, ok := obj.(*controlplanev1.KubeadmControlPlaneTemplate) 58 if !ok { 59 return apierrors.NewBadRequest(fmt.Sprintf("expected a KubeadmControlPlaneTemplate but got a %T", obj)) 60 } 61 62 k.Spec.Template.Spec.KubeadmConfigSpec.Default() 63 64 k.Spec.Template.Spec.RolloutStrategy = defaultRolloutStrategy(k.Spec.Template.Spec.RolloutStrategy) 65 66 return nil 67 } 68 69 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. 70 func (webhook *KubeadmControlPlaneTemplate) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) { 71 k, ok := obj.(*controlplanev1.KubeadmControlPlaneTemplate) 72 if !ok { 73 return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a KubeadmControlPlaneTemplate but got a %T", obj)) 74 } 75 76 // NOTE: KubeadmControlPlaneTemplate is behind ClusterTopology feature gate flag; the web hook 77 // must prevent creating new objects in case the feature flag is disabled. 78 if !feature.Gates.Enabled(feature.ClusterTopology) { 79 return nil, field.Forbidden( 80 field.NewPath("spec"), 81 "can be set only if the ClusterTopology feature flag is enabled", 82 ) 83 } 84 85 spec := k.Spec.Template.Spec 86 allErrs := validateKubeadmControlPlaneTemplateResourceSpec(spec, field.NewPath("spec", "template", "spec")) 87 allErrs = append(allErrs, validateClusterConfiguration(nil, spec.KubeadmConfigSpec.ClusterConfiguration, field.NewPath("spec", "template", "spec", "kubeadmConfigSpec", "clusterConfiguration"))...) 88 allErrs = append(allErrs, spec.KubeadmConfigSpec.Validate(field.NewPath("spec", "template", "spec", "kubeadmConfigSpec"))...) 89 // Validate the metadata of the KubeadmControlPlaneTemplateResource 90 allErrs = append(allErrs, k.Spec.Template.ObjectMeta.Validate(field.NewPath("spec", "template", "metadata"))...) 91 if len(allErrs) > 0 { 92 return nil, apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("KubeadmControlPlaneTemplate").GroupKind(), k.Name, allErrs) 93 } 94 return nil, nil 95 } 96 97 // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. 98 func (webhook *KubeadmControlPlaneTemplate) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { 99 var allErrs field.ErrorList 100 101 oldK, ok := oldObj.(*controlplanev1.KubeadmControlPlaneTemplate) 102 if !ok { 103 return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a KubeadmControlPlaneTemplate but got a %T", oldObj)) 104 } 105 106 newK, ok := newObj.(*controlplanev1.KubeadmControlPlaneTemplate) 107 if !ok { 108 return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a KubeadmControlPlaneTemplate but got a %T", newObj)) 109 } 110 111 if !reflect.DeepEqual(newK.Spec.Template.Spec, oldK.Spec.Template.Spec) { 112 allErrs = append(allErrs, 113 field.Invalid(field.NewPath("spec", "template", "spec"), newK, kubeadmControlPlaneTemplateImmutableMsg), 114 ) 115 } 116 117 if len(allErrs) == 0 { 118 return nil, nil 119 } 120 return nil, apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("KubeadmControlPlaneTemplate").GroupKind(), newK.Name, allErrs) 121 } 122 123 // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. 124 func (webhook *KubeadmControlPlaneTemplate) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { 125 return nil, nil 126 } 127 128 // validateKubeadmControlPlaneTemplateResourceSpec is a copy of validateKubeadmControlPlaneSpec which 129 // only validates the fields in KubeadmControlPlaneTemplateResourceSpec we care about. 130 func validateKubeadmControlPlaneTemplateResourceSpec(s controlplanev1.KubeadmControlPlaneTemplateResourceSpec, pathPrefix *field.Path) field.ErrorList { 131 allErrs := field.ErrorList{} 132 133 allErrs = append(allErrs, validateRolloutBefore(s.RolloutBefore, pathPrefix.Child("rolloutBefore"))...) 134 allErrs = append(allErrs, validateRolloutStrategy(s.RolloutStrategy, nil, pathPrefix.Child("rolloutStrategy"))...) 135 136 if s.MachineTemplate != nil { 137 // Validate the metadata of the MachineTemplate 138 allErrs = append(allErrs, s.MachineTemplate.ObjectMeta.Validate(pathPrefix.Child("machineTemplate", "metadata"))...) 139 } 140 141 return allErrs 142 }