sigs.k8s.io/cluster-api@v1.7.1/internal/webhooks/machine_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 "testing" 21 22 . "github.com/onsi/gomega" 23 corev1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/utils/ptr" 26 27 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 28 "sigs.k8s.io/cluster-api/internal/webhooks/util" 29 ) 30 31 func TestMachineDefault(t *testing.T) { 32 g := NewWithT(t) 33 34 m := &clusterv1.Machine{ 35 ObjectMeta: metav1.ObjectMeta{ 36 Namespace: "foobar", 37 }, 38 Spec: clusterv1.MachineSpec{ 39 Bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}}, 40 Version: ptr.To("1.17.5"), 41 }, 42 } 43 44 webhook := &Machine{} 45 46 t.Run("for Machine", util.CustomDefaultValidateTest(ctx, m, webhook)) 47 g.Expect(webhook.Default(ctx, m)).To(Succeed()) 48 49 g.Expect(m.Labels[clusterv1.ClusterNameLabel]).To(Equal(m.Spec.ClusterName)) 50 g.Expect(m.Spec.Bootstrap.ConfigRef.Namespace).To(Equal(m.Namespace)) 51 g.Expect(m.Spec.InfrastructureRef.Namespace).To(Equal(m.Namespace)) 52 g.Expect(*m.Spec.Version).To(Equal("v1.17.5")) 53 g.Expect(m.Spec.NodeDeletionTimeout.Duration).To(Equal(defaultNodeDeletionTimeout)) 54 } 55 56 func TestMachineBootstrapValidation(t *testing.T) { 57 tests := []struct { 58 name string 59 bootstrap clusterv1.Bootstrap 60 expectErr bool 61 }{ 62 { 63 name: "should return error if configref and data are nil", 64 bootstrap: clusterv1.Bootstrap{ConfigRef: nil, DataSecretName: nil}, 65 expectErr: true, 66 }, 67 { 68 name: "should not return error if dataSecretName is set", 69 bootstrap: clusterv1.Bootstrap{ConfigRef: nil, DataSecretName: ptr.To("test")}, 70 expectErr: false, 71 }, 72 { 73 name: "should not return error if config ref is set", 74 bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}, DataSecretName: nil}, 75 expectErr: false, 76 }, 77 } 78 79 for _, tt := range tests { 80 t.Run(tt.name, func(t *testing.T) { 81 g := NewWithT(t) 82 m := &clusterv1.Machine{ 83 Spec: clusterv1.MachineSpec{Bootstrap: tt.bootstrap}, 84 } 85 webhook := &Machine{} 86 87 if tt.expectErr { 88 warnings, err := webhook.ValidateCreate(ctx, m) 89 g.Expect(err).To(HaveOccurred()) 90 g.Expect(warnings).To(BeEmpty()) 91 warnings, err = webhook.ValidateUpdate(ctx, m, m) 92 g.Expect(err).To(HaveOccurred()) 93 g.Expect(warnings).To(BeEmpty()) 94 } else { 95 warnings, err := webhook.ValidateCreate(ctx, m) 96 g.Expect(err).ToNot(HaveOccurred()) 97 g.Expect(warnings).To(BeEmpty()) 98 warnings, err = webhook.ValidateUpdate(ctx, m, m) 99 g.Expect(err).ToNot(HaveOccurred()) 100 g.Expect(warnings).To(BeEmpty()) 101 } 102 }) 103 } 104 } 105 106 func TestMachineNamespaceValidation(t *testing.T) { 107 tests := []struct { 108 name string 109 expectErr bool 110 bootstrap clusterv1.Bootstrap 111 infraRef corev1.ObjectReference 112 namespace string 113 }{ 114 { 115 name: "should succeed if all namespaces match", 116 expectErr: false, 117 namespace: "foobar", 118 bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar"}}, 119 infraRef: corev1.ObjectReference{Namespace: "foobar"}, 120 }, 121 { 122 name: "should return error if namespace and bootstrap namespace don't match", 123 expectErr: true, 124 namespace: "foobar", 125 bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar123"}}, 126 infraRef: corev1.ObjectReference{Namespace: "foobar"}, 127 }, 128 { 129 name: "should return error if namespace and infrastructure ref namespace don't match", 130 expectErr: true, 131 namespace: "foobar", 132 bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar"}}, 133 infraRef: corev1.ObjectReference{Namespace: "foobar123"}, 134 }, 135 { 136 name: "should return error if no namespaces match", 137 expectErr: true, 138 namespace: "foobar1", 139 bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar2"}}, 140 infraRef: corev1.ObjectReference{Namespace: "foobar3"}, 141 }, 142 } 143 144 for _, tt := range tests { 145 t.Run(tt.name, func(t *testing.T) { 146 g := NewWithT(t) 147 148 m := &clusterv1.Machine{ 149 ObjectMeta: metav1.ObjectMeta{Namespace: tt.namespace}, 150 Spec: clusterv1.MachineSpec{Bootstrap: tt.bootstrap, InfrastructureRef: tt.infraRef}, 151 } 152 webhook := &Machine{} 153 154 if tt.expectErr { 155 warnings, err := webhook.ValidateCreate(ctx, m) 156 g.Expect(err).To(HaveOccurred()) 157 g.Expect(warnings).To(BeEmpty()) 158 warnings, err = webhook.ValidateUpdate(ctx, m, m) 159 g.Expect(err).To(HaveOccurred()) 160 g.Expect(warnings).To(BeEmpty()) 161 } else { 162 warnings, err := webhook.ValidateCreate(ctx, m) 163 g.Expect(err).ToNot(HaveOccurred()) 164 g.Expect(warnings).To(BeEmpty()) 165 warnings, err = webhook.ValidateUpdate(ctx, m, m) 166 g.Expect(err).ToNot(HaveOccurred()) 167 g.Expect(warnings).To(BeEmpty()) 168 } 169 }) 170 } 171 } 172 173 func TestMachineClusterNameImmutable(t *testing.T) { 174 tests := []struct { 175 name string 176 oldClusterName string 177 newClusterName string 178 expectErr bool 179 }{ 180 { 181 name: "when the cluster name has not changed", 182 oldClusterName: "foo", 183 newClusterName: "foo", 184 expectErr: false, 185 }, 186 { 187 name: "when the cluster name has changed", 188 oldClusterName: "foo", 189 newClusterName: "bar", 190 expectErr: true, 191 }, 192 } 193 194 for _, tt := range tests { 195 t.Run(tt.name, func(t *testing.T) { 196 g := NewWithT(t) 197 198 newMachine := &clusterv1.Machine{ 199 Spec: clusterv1.MachineSpec{ 200 ClusterName: tt.newClusterName, 201 Bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}}, 202 }, 203 } 204 oldMachine := &clusterv1.Machine{ 205 Spec: clusterv1.MachineSpec{ 206 ClusterName: tt.oldClusterName, 207 Bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}}, 208 }, 209 } 210 211 warnings, err := (&Machine{}).ValidateUpdate(ctx, oldMachine, newMachine) 212 if tt.expectErr { 213 g.Expect(err).To(HaveOccurred()) 214 g.Expect(warnings).To(BeEmpty()) 215 } else { 216 g.Expect(err).ToNot(HaveOccurred()) 217 g.Expect(warnings).To(BeEmpty()) 218 } 219 }) 220 } 221 } 222 223 func TestMachineVersionValidation(t *testing.T) { 224 tests := []struct { 225 name string 226 version string 227 expectErr bool 228 }{ 229 { 230 name: "should succeed when given a valid semantic version with prepended 'v'", 231 version: "v1.17.2", 232 expectErr: false, 233 }, 234 { 235 name: "should return error when given a valid semantic version without 'v'", 236 version: "1.17.2", 237 expectErr: true, 238 }, 239 { 240 name: "should return error when given an invalid semantic version", 241 version: "1", 242 expectErr: true, 243 }, 244 { 245 name: "should return error when given an invalid semantic version", 246 version: "v1", 247 expectErr: true, 248 }, 249 { 250 name: "should return error when given an invalid semantic version", 251 version: "wrong_version", 252 expectErr: true, 253 }, 254 } 255 256 for i := range tests { 257 tt := tests[i] 258 t.Run(tt.name, func(t *testing.T) { 259 g := NewWithT(t) 260 261 m := &clusterv1.Machine{ 262 Spec: clusterv1.MachineSpec{ 263 Version: &tt.version, 264 Bootstrap: clusterv1.Bootstrap{ConfigRef: nil, DataSecretName: ptr.To("test")}, 265 }, 266 } 267 webhook := &Machine{} 268 269 if tt.expectErr { 270 warnings, err := webhook.ValidateCreate(ctx, m) 271 g.Expect(err).To(HaveOccurred()) 272 g.Expect(warnings).To(BeEmpty()) 273 warnings, err = webhook.ValidateUpdate(ctx, m, m) 274 g.Expect(err).To(HaveOccurred()) 275 g.Expect(warnings).To(BeEmpty()) 276 } else { 277 warnings, err := webhook.ValidateCreate(ctx, m) 278 g.Expect(err).ToNot(HaveOccurred()) 279 g.Expect(warnings).To(BeEmpty()) 280 warnings, err = webhook.ValidateUpdate(ctx, m, m) 281 g.Expect(err).ToNot(HaveOccurred()) 282 g.Expect(warnings).To(BeEmpty()) 283 } 284 }) 285 } 286 }