sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/webhooks/kubeadmcontrolplanetemplate_test.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 "strings" 21 "testing" 22 "time" 23 24 . "github.com/onsi/gomega" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 utilfeature "k8s.io/component-base/featuregate/testing" 27 28 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 29 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" 30 controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" 31 "sigs.k8s.io/cluster-api/feature" 32 "sigs.k8s.io/cluster-api/internal/webhooks/util" 33 ) 34 35 func TestKubeadmControlPlaneTemplateDefault(t *testing.T) { 36 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 37 38 g := NewWithT(t) 39 40 kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{ 41 ObjectMeta: metav1.ObjectMeta{ 42 Namespace: "foo", 43 }, 44 Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{ 45 Template: controlplanev1.KubeadmControlPlaneTemplateResource{ 46 Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{ 47 MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{ 48 NodeDrainTimeout: &metav1.Duration{Duration: 10 * time.Second}, 49 }, 50 }, 51 }, 52 }, 53 } 54 updateDefaultingValidationKCPTemplate := kcpTemplate.DeepCopy() 55 updateDefaultingValidationKCPTemplate.Spec.Template.Spec.MachineTemplate.NodeDrainTimeout = &metav1.Duration{Duration: 20 * time.Second} 56 webhook := &KubeadmControlPlaneTemplate{} 57 t.Run("for KubeadmControlPlaneTemplate", util.CustomDefaultValidateTest(ctx, updateDefaultingValidationKCPTemplate, webhook)) 58 g.Expect(webhook.Default(ctx, kcpTemplate)).To(Succeed()) 59 60 g.Expect(kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.Format).To(Equal(bootstrapv1.CloudConfig)) 61 g.Expect(kcpTemplate.Spec.Template.Spec.RolloutStrategy.Type).To(Equal(controlplanev1.RollingUpdateStrategyType)) 62 g.Expect(kcpTemplate.Spec.Template.Spec.RolloutStrategy.RollingUpdate.MaxSurge.IntVal).To(Equal(int32(1))) 63 } 64 65 func TestKubeadmControlPlaneTemplateValidationFeatureGateEnabled(t *testing.T) { 66 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 67 68 t.Run("create kubeadmcontrolplanetemplate should pass if gate enabled and valid kubeadmcontrolplanetemplate", func(t *testing.T) { 69 testnamespace := "test" 70 g := NewWithT(t) 71 kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{ 72 ObjectMeta: metav1.ObjectMeta{ 73 Name: "kubeadmcontrolplanetemplate-test", 74 Namespace: testnamespace, 75 }, 76 Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{ 77 Template: controlplanev1.KubeadmControlPlaneTemplateResource{ 78 Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{ 79 MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{ 80 NodeDrainTimeout: &metav1.Duration{Duration: time.Second}, 81 }, 82 }, 83 }, 84 }, 85 } 86 webhook := &KubeadmControlPlaneTemplate{} 87 warnings, err := webhook.ValidateCreate(ctx, kcpTemplate) 88 g.Expect(err).ToNot(HaveOccurred()) 89 g.Expect(warnings).To(BeEmpty()) 90 }) 91 } 92 93 func TestKubeadmControlPlaneTemplateValidationFeatureGateDisabled(t *testing.T) { 94 // NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create KubeadmControlPlaneTemplate. 95 t.Run("create kubeadmcontrolplanetemplate should not pass if gate disabled and valid kubeadmcontrolplanetemplate", func(t *testing.T) { 96 testnamespace := "test" 97 g := NewWithT(t) 98 kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: "kubeadmcontrolplanetemplate-test", 101 Namespace: testnamespace, 102 }, 103 Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{ 104 Template: controlplanev1.KubeadmControlPlaneTemplateResource{ 105 Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{ 106 MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{ 107 NodeDrainTimeout: &metav1.Duration{Duration: time.Second}, 108 }, 109 }, 110 }, 111 }, 112 } 113 webhook := &KubeadmControlPlaneTemplate{} 114 warnings, err := webhook.ValidateCreate(ctx, kcpTemplate) 115 g.Expect(err).To(HaveOccurred()) 116 g.Expect(warnings).To(BeEmpty()) 117 }) 118 } 119 120 func TestKubeadmControlPlaneTemplateValidationMetadata(t *testing.T) { 121 t.Run("create kubeadmcontrolplanetemplate should not pass if metadata is invalid", func(t *testing.T) { 122 g := NewWithT(t) 123 kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{ 124 Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{ 125 Template: controlplanev1.KubeadmControlPlaneTemplateResource{ 126 ObjectMeta: clusterv1.ObjectMeta{ 127 Labels: map[string]string{ 128 "foo": "$invalid-key", 129 "bar": strings.Repeat("a", 64) + "too-long-value", 130 "/invalid-key": "foo", 131 }, 132 Annotations: map[string]string{ 133 "/invalid-key": "foo", 134 }, 135 }, 136 Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{ 137 MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{ 138 ObjectMeta: clusterv1.ObjectMeta{ 139 Labels: map[string]string{ 140 "foo": "$invalid-key", 141 "bar": strings.Repeat("a", 64) + "too-long-value", 142 "/invalid-key": "foo", 143 }, 144 Annotations: map[string]string{ 145 "/invalid-key": "foo", 146 }, 147 }, 148 }, 149 }, 150 }, 151 }, 152 } 153 webhook := &KubeadmControlPlaneTemplate{} 154 warnings, err := webhook.ValidateCreate(ctx, kcpTemplate) 155 g.Expect(err).To(HaveOccurred()) 156 g.Expect(warnings).To(BeEmpty()) 157 }) 158 }