sigs.k8s.io/cluster-api@v1.6.3/bootstrap/util/configowner_test.go (about) 1 /* 2 Copyright 2019 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 util 18 19 import ( 20 "context" 21 "testing" 22 23 . "github.com/onsi/gomega" 24 corev1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/utils/pointer" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 "sigs.k8s.io/controller-runtime/pkg/client/fake" 31 32 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 33 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" 34 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 35 "sigs.k8s.io/cluster-api/feature" 36 ) 37 38 func TestGetConfigOwner(t *testing.T) { 39 doTests := func(t *testing.T, getFn func(context.Context, client.Client, metav1.Object) (*ConfigOwner, error)) { 40 t.Helper() 41 42 t.Run("should get the owner when present (Machine)", func(t *testing.T) { 43 g := NewWithT(t) 44 myMachine := &clusterv1.Machine{ 45 ObjectMeta: metav1.ObjectMeta{ 46 Name: "my-machine", 47 Namespace: metav1.NamespaceDefault, 48 Labels: map[string]string{ 49 clusterv1.MachineControlPlaneLabel: "", 50 }, 51 }, 52 Spec: clusterv1.MachineSpec{ 53 ClusterName: "my-cluster", 54 Bootstrap: clusterv1.Bootstrap{ 55 DataSecretName: pointer.String("my-data-secret"), 56 }, 57 Version: pointer.String("v1.19.6"), 58 }, 59 Status: clusterv1.MachineStatus{ 60 InfrastructureReady: true, 61 }, 62 } 63 64 c := fake.NewClientBuilder().WithObjects(myMachine).Build() 65 obj := &bootstrapv1.KubeadmConfig{ 66 ObjectMeta: metav1.ObjectMeta{ 67 OwnerReferences: []metav1.OwnerReference{ 68 { 69 Kind: "Machine", 70 APIVersion: clusterv1.GroupVersion.String(), 71 Name: "my-machine", 72 }, 73 }, 74 Namespace: metav1.NamespaceDefault, 75 Name: "my-resource-owned-by-machine", 76 }, 77 } 78 configOwner, err := getFn(ctx, c, obj) 79 g.Expect(err).ToNot(HaveOccurred()) 80 g.Expect(configOwner).ToNot(BeNil()) 81 g.Expect(configOwner.ClusterName()).To(BeEquivalentTo("my-cluster")) 82 g.Expect(configOwner.IsInfrastructureReady()).To(BeTrue()) 83 g.Expect(configOwner.IsControlPlaneMachine()).To(BeTrue()) 84 g.Expect(configOwner.IsMachinePool()).To(BeFalse()) 85 g.Expect(configOwner.KubernetesVersion()).To(Equal("v1.19.6")) 86 g.Expect(*configOwner.DataSecretName()).To(BeEquivalentTo("my-data-secret")) 87 }) 88 89 t.Run("should get the owner when present (MachinePool)", func(t *testing.T) { 90 _ = feature.MutableGates.Set("MachinePool=true") 91 92 g := NewWithT(t) 93 myPool := &expv1.MachinePool{ 94 ObjectMeta: metav1.ObjectMeta{ 95 Name: "my-machine-pool", 96 Namespace: metav1.NamespaceDefault, 97 Labels: map[string]string{ 98 clusterv1.MachineControlPlaneLabel: "", 99 }, 100 }, 101 Spec: expv1.MachinePoolSpec{ 102 ClusterName: "my-cluster", 103 Template: clusterv1.MachineTemplateSpec{ 104 Spec: clusterv1.MachineSpec{ 105 Version: pointer.String("v1.19.6"), 106 }, 107 }, 108 }, 109 Status: expv1.MachinePoolStatus{ 110 InfrastructureReady: true, 111 }, 112 } 113 114 c := fake.NewClientBuilder().WithObjects(myPool).Build() 115 obj := &bootstrapv1.KubeadmConfig{ 116 ObjectMeta: metav1.ObjectMeta{ 117 OwnerReferences: []metav1.OwnerReference{ 118 { 119 Kind: "MachinePool", 120 APIVersion: expv1.GroupVersion.String(), 121 Name: "my-machine-pool", 122 }, 123 }, 124 Namespace: metav1.NamespaceDefault, 125 Name: "my-resource-owned-by-machine-pool", 126 }, 127 } 128 configOwner, err := getFn(ctx, c, obj) 129 g.Expect(err).ToNot(HaveOccurred()) 130 g.Expect(configOwner).ToNot(BeNil()) 131 g.Expect(configOwner.ClusterName()).To(BeEquivalentTo("my-cluster")) 132 g.Expect(configOwner.IsInfrastructureReady()).To(BeTrue()) 133 g.Expect(configOwner.IsControlPlaneMachine()).To(BeFalse()) 134 g.Expect(configOwner.IsMachinePool()).To(BeTrue()) 135 g.Expect(configOwner.KubernetesVersion()).To(Equal("v1.19.6")) 136 g.Expect(configOwner.DataSecretName()).To(BeNil()) 137 }) 138 139 t.Run("return an error when not found", func(t *testing.T) { 140 g := NewWithT(t) 141 c := fake.NewClientBuilder().Build() 142 obj := &bootstrapv1.KubeadmConfig{ 143 ObjectMeta: metav1.ObjectMeta{ 144 OwnerReferences: []metav1.OwnerReference{ 145 { 146 Kind: "Machine", 147 APIVersion: clusterv1.GroupVersion.String(), 148 Name: "my-machine", 149 }, 150 }, 151 Namespace: metav1.NamespaceDefault, 152 Name: "my-resource-owned-by-machine", 153 }, 154 } 155 _, err := getFn(ctx, c, obj) 156 g.Expect(err).To(HaveOccurred()) 157 }) 158 159 t.Run("return nothing when there is no owner", func(t *testing.T) { 160 g := NewWithT(t) 161 c := fake.NewClientBuilder().Build() 162 obj := &bootstrapv1.KubeadmConfig{ 163 ObjectMeta: metav1.ObjectMeta{ 164 OwnerReferences: []metav1.OwnerReference{}, 165 Namespace: metav1.NamespaceDefault, 166 Name: "my-resource-owned-by-machine", 167 }, 168 } 169 configOwner, err := getFn(ctx, c, obj) 170 g.Expect(err).ToNot(HaveOccurred()) 171 g.Expect(configOwner).To(BeNil()) 172 }) 173 } 174 t.Run("uncached", func(t *testing.T) { 175 doTests(t, GetConfigOwner) 176 }) 177 t.Run("cached", func(t *testing.T) { 178 doTests(t, GetTypedConfigOwner) 179 }) 180 } 181 182 func TestHasNodeRefs(t *testing.T) { 183 t.Run("should return false if there is no nodeRef", func(t *testing.T) { 184 g := NewWithT(t) 185 machine := &clusterv1.Machine{ 186 TypeMeta: metav1.TypeMeta{ 187 Kind: "Machine", 188 }, 189 ObjectMeta: metav1.ObjectMeta{ 190 Name: "machine-name", 191 Namespace: metav1.NamespaceDefault, 192 }, 193 } 194 content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&machine) 195 if err != nil { 196 g.Fail(err.Error()) 197 } 198 unstructuredOwner := unstructured.Unstructured{} 199 unstructuredOwner.SetUnstructuredContent(content) 200 co := ConfigOwner{&unstructuredOwner} 201 result := co.HasNodeRefs() 202 g.Expect(result).To(BeFalse()) 203 }) 204 t.Run("should return true if there is a nodeRef for Machine", func(t *testing.T) { 205 g := NewWithT(t) 206 machine := &clusterv1.Machine{ 207 TypeMeta: metav1.TypeMeta{ 208 Kind: "Machine", 209 }, 210 ObjectMeta: metav1.ObjectMeta{ 211 Name: "machine-name", 212 Namespace: metav1.NamespaceDefault, 213 }, 214 Status: clusterv1.MachineStatus{ 215 InfrastructureReady: true, 216 NodeRef: &corev1.ObjectReference{ 217 Kind: "Node", 218 Namespace: metav1.NamespaceDefault, 219 Name: "node-0", 220 }, 221 }, 222 } 223 content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&machine) 224 if err != nil { 225 g.Fail(err.Error()) 226 } 227 unstructuredOwner := unstructured.Unstructured{} 228 unstructuredOwner.SetUnstructuredContent(content) 229 co := ConfigOwner{&unstructuredOwner} 230 231 result := co.HasNodeRefs() 232 g.Expect(result).To(BeTrue()) 233 }) 234 t.Run("should return false if nodes are missing from MachinePool", func(t *testing.T) { 235 g := NewWithT(t) 236 machinePools := []expv1.MachinePool{ 237 { 238 // No replicas specified (default is 1). No nodeRefs either. 239 TypeMeta: metav1.TypeMeta{ 240 Kind: "MachinePool", 241 }, 242 ObjectMeta: metav1.ObjectMeta{ 243 Namespace: metav1.NamespaceDefault, 244 Name: "machine-pool-name", 245 }, 246 }, 247 { 248 // 1 replica but no nodeRefs 249 TypeMeta: metav1.TypeMeta{ 250 Kind: "MachinePool", 251 }, 252 ObjectMeta: metav1.ObjectMeta{ 253 Namespace: metav1.NamespaceDefault, 254 Name: "machine-pool-name", 255 }, 256 Spec: expv1.MachinePoolSpec{ 257 Replicas: pointer.Int32(1), 258 }, 259 }, 260 { 261 // 2 replicas but only 1 nodeRef 262 TypeMeta: metav1.TypeMeta{ 263 Kind: "MachinePool", 264 }, 265 ObjectMeta: metav1.ObjectMeta{ 266 Namespace: metav1.NamespaceDefault, 267 Name: "machine-pool-name", 268 }, 269 Spec: expv1.MachinePoolSpec{ 270 Replicas: pointer.Int32(2), 271 }, 272 Status: expv1.MachinePoolStatus{ 273 NodeRefs: []corev1.ObjectReference{ 274 { 275 Kind: "Node", 276 Namespace: metav1.NamespaceDefault, 277 Name: "node-0", 278 }, 279 }, 280 }, 281 }, 282 } 283 284 for i := range machinePools { 285 content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&machinePools[i]) 286 if err != nil { 287 g.Fail(err.Error()) 288 } 289 unstructuredOwner := unstructured.Unstructured{} 290 unstructuredOwner.SetUnstructuredContent(content) 291 co := ConfigOwner{&unstructuredOwner} 292 293 result := co.HasNodeRefs() 294 g.Expect(result).To(BeFalse()) 295 } 296 }) 297 t.Run("should return true if MachinePool has nodeRefs for all replicas", func(t *testing.T) { 298 g := NewWithT(t) 299 machinePools := []expv1.MachinePool{ 300 { 301 // 1 replica (default) and 1 nodeRef 302 TypeMeta: metav1.TypeMeta{ 303 Kind: "MachinePool", 304 }, 305 ObjectMeta: metav1.ObjectMeta{ 306 Namespace: metav1.NamespaceDefault, 307 Name: "machine-pool-name", 308 }, 309 Status: expv1.MachinePoolStatus{ 310 NodeRefs: []corev1.ObjectReference{ 311 { 312 Kind: "Node", 313 Namespace: metav1.NamespaceDefault, 314 Name: "node-0", 315 }, 316 }, 317 }, 318 }, 319 { 320 // 2 replicas and nodeRefs 321 TypeMeta: metav1.TypeMeta{ 322 Kind: "MachinePool", 323 }, 324 ObjectMeta: metav1.ObjectMeta{ 325 Namespace: metav1.NamespaceDefault, 326 Name: "machine-pool-name", 327 }, 328 Spec: expv1.MachinePoolSpec{ 329 Replicas: pointer.Int32(2), 330 }, 331 Status: expv1.MachinePoolStatus{ 332 NodeRefs: []corev1.ObjectReference{ 333 { 334 Kind: "Node", 335 Namespace: metav1.NamespaceDefault, 336 Name: "node-0", 337 }, 338 { 339 Kind: "Node", 340 Namespace: metav1.NamespaceDefault, 341 Name: "node-1", 342 }, 343 }, 344 }, 345 }, 346 { 347 // 0 replicas and 0 nodeRef 348 TypeMeta: metav1.TypeMeta{ 349 Kind: "MachinePool", 350 }, 351 ObjectMeta: metav1.ObjectMeta{ 352 Namespace: metav1.NamespaceDefault, 353 Name: "machine-pool-name", 354 }, 355 Spec: expv1.MachinePoolSpec{ 356 Replicas: pointer.Int32(0), 357 }, 358 }, 359 } 360 361 for i := range machinePools { 362 content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&machinePools[i]) 363 if err != nil { 364 g.Fail(err.Error()) 365 } 366 unstructuredOwner := unstructured.Unstructured{} 367 unstructuredOwner.SetUnstructuredContent(content) 368 co := ConfigOwner{&unstructuredOwner} 369 370 result := co.HasNodeRefs() 371 g.Expect(result).To(BeTrue()) 372 } 373 }) 374 }