sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/cluster/components_test.go (about) 1 /* 2 Copyright 2020 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 cluster 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 . "github.com/onsi/gomega" 26 corev1 "k8s.io/api/core/v1" 27 rbacv1 "k8s.io/api/rbac/v1" 28 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 29 apierrors "k8s.io/apimachinery/pkg/api/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 34 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 35 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" 36 "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/scheme" 37 "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test" 38 ) 39 40 func Test_providerComponents_Delete(t *testing.T) { 41 labels := map[string]string{ 42 clusterv1.ProviderNameLabel: "infrastructure-infra", 43 } 44 45 crd := unstructured.Unstructured{} 46 crd.SetAPIVersion("apiextensions.k8s.io/v1beta1") 47 crd.SetKind("CustomResourceDefinition") 48 crd.SetName("crd1") 49 crd.SetLabels(labels) 50 providerInventory := unstructured.Unstructured{} 51 providerInventory.SetAPIVersion(clusterctlv1.GroupVersion.String()) 52 providerInventory.SetKind("Provider") 53 providerInventory.SetName("providerOne") 54 providerInventory.SetLabels(labels) 55 mutatingWebhook := unstructured.Unstructured{} 56 mutatingWebhook.SetAPIVersion("admissionregistration.k8s.io/v1beta1") 57 mutatingWebhook.SetKind("MutatingWebhookConfiguration") 58 mutatingWebhook.SetName("mwh1") 59 mutatingWebhook.SetLabels(labels) 60 61 initObjs := []client.Object{ 62 // Namespace (should be deleted only if includeNamespace) 63 &corev1.Namespace{ 64 TypeMeta: metav1.TypeMeta{ 65 Kind: "Namespace", 66 }, 67 ObjectMeta: metav1.ObjectMeta{ 68 Name: "ns1", 69 Labels: labels, 70 }, 71 }, 72 // A namespaced provider component (should always be deleted) 73 &corev1.Pod{ 74 TypeMeta: metav1.TypeMeta{ 75 Kind: "Pod", 76 }, 77 ObjectMeta: metav1.ObjectMeta{ 78 Namespace: "ns1", 79 Name: "pod1", 80 Labels: labels, 81 }, 82 }, 83 // Another object in the namespace but not belonging to the provider (should go away only when deleting the namespace) 84 &corev1.Pod{ 85 TypeMeta: metav1.TypeMeta{ 86 Kind: "Pod", 87 }, 88 ObjectMeta: metav1.ObjectMeta{ 89 Namespace: "ns1", 90 Name: "pod2", 91 }, 92 }, 93 // CRDs (should be deleted only if includeCRD) 94 &crd, 95 // Inventory should be deleted ony if skipInventory 96 &providerInventory, 97 &mutatingWebhook, 98 // A cluster-wide provider component (should always be deleted) 99 &rbacv1.ClusterRole{ 100 TypeMeta: metav1.TypeMeta{ 101 Kind: "ClusterRole", 102 }, 103 ObjectMeta: metav1.ObjectMeta{ 104 Name: "ns1-cluster-role", // global objects belonging to the provider have a namespace prefix. 105 Labels: labels, 106 }, 107 }, 108 // Another cluster-wide object (should never be deleted) 109 &rbacv1.ClusterRole{ 110 TypeMeta: metav1.TypeMeta{ 111 Kind: "ClusterRole", 112 }, 113 ObjectMeta: metav1.ObjectMeta{ 114 Name: "some-cluster-role", 115 }, 116 }, 117 // Another object out of the provider namespace (should never be deleted) 118 &corev1.Pod{ 119 TypeMeta: metav1.TypeMeta{ 120 Kind: "Pod", 121 }, 122 ObjectMeta: metav1.ObjectMeta{ 123 Namespace: "ns2", 124 Name: "pod3", 125 Labels: labels, 126 }, 127 }, 128 } 129 130 type args struct { 131 provider clusterctlv1.Provider 132 includeNamespace bool 133 includeCRD bool 134 skipInventory bool 135 } 136 137 type wantDiff struct { 138 object corev1.ObjectReference 139 deleted bool 140 } 141 142 tests := []struct { 143 name string 144 args args 145 wantDiff []wantDiff 146 wantErr bool 147 }{ 148 { 149 name: "Delete provider while preserving Namespace and CRDs and providerInventory", 150 args: args{ 151 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 152 includeNamespace: false, 153 includeCRD: false, 154 skipInventory: true, 155 }, 156 wantDiff: []wantDiff{ 157 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Namespace", Name: "ns1"}, deleted: false}, // namespace should be preserved 158 {object: corev1.ObjectReference{APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition", Name: "crd1"}, deleted: false}, // crd should be preserved 159 {object: corev1.ObjectReference{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration", Name: "mwh1"}, deleted: true}, // MutatingWebhookConfiguration goes away with the controller 160 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod1"}, deleted: true}, // provider components should be deleted 161 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod2"}, deleted: false}, // other objects in the namespace should not be deleted 162 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns2", Name: "pod3"}, deleted: false}, // this object is in another namespace, and should never be touched by delete 163 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "ns1-cluster-role"}, deleted: true}, // cluster-wide provider components should be deleted 164 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "some-cluster-role"}, deleted: false}, // other cluster-wide objects should be preserved 165 {object: corev1.ObjectReference{APIVersion: clusterctlv1.GroupVersion.String(), Kind: "Provider", Name: "providerOne"}, deleted: false}, // providerInventory should be preserved 166 }, 167 wantErr: false, 168 }, 169 { 170 name: "Delete provider and provider namespace, while preserving CRDs and providerInventory", 171 args: args{ 172 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 173 includeNamespace: true, 174 includeCRD: false, 175 skipInventory: true, 176 }, 177 wantDiff: []wantDiff{ 178 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Namespace", Name: "ns1"}, deleted: true}, // namespace should be deleted 179 {object: corev1.ObjectReference{APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition", Name: "crd1"}, deleted: false}, // crd should be preserved 180 {object: corev1.ObjectReference{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration", Name: "mwh1"}, deleted: true}, // MutatingWebhookConfiguration goes away with the controller 181 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod1"}, deleted: true}, // provider components should be deleted 182 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod2"}, deleted: true}, // other objects in the namespace goes away when deleting the namespace 183 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns2", Name: "pod3"}, deleted: false}, // this object is in another namespace, and should never be touched by delete 184 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "ns1-cluster-role"}, deleted: true}, // cluster-wide provider components should be deleted 185 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "some-cluster-role"}, deleted: false}, // other cluster-wide objects should be preserved 186 {object: corev1.ObjectReference{APIVersion: clusterctlv1.GroupVersion.String(), Kind: "Provider", Name: "providerOne"}, deleted: false}, // providerInventory should be preserved 187 }, 188 wantErr: false, 189 }, 190 { 191 name: "Delete provider and provider CRDs, while preserving the provider namespace and the providerInventory", 192 args: args{ 193 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 194 includeNamespace: false, 195 includeCRD: true, 196 skipInventory: true, 197 }, 198 wantDiff: []wantDiff{ 199 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Namespace", Name: "ns1"}, deleted: false}, // namespace should be preserved 200 {object: corev1.ObjectReference{APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition", Name: "crd1"}, deleted: true}, // crd should be deleted 201 {object: corev1.ObjectReference{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration", Name: "mwh1"}, deleted: true}, // MutatingWebhookConfiguration should be deleted 202 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod1"}, deleted: true}, // provider components should be deleted 203 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod2"}, deleted: false}, // other objects in the namespace should not be deleted 204 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns2", Name: "pod3"}, deleted: false}, // this object is in another namespace, and should never be touched by delete 205 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "ns1-cluster-role"}, deleted: true}, // cluster-wide provider components should be deleted 206 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "some-cluster-role"}, deleted: false}, // other cluster-wide objects should be preserved 207 {object: corev1.ObjectReference{APIVersion: clusterctlv1.GroupVersion.String(), Kind: "Provider", Name: "providerOne"}, deleted: false}, // providerInventory should be preserved 208 }, 209 wantErr: false, 210 }, 211 { 212 name: "Delete providerInventory and provider while preserving provider CRDs and provider namespace", 213 args: args{ 214 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 215 includeNamespace: false, 216 includeCRD: false, 217 skipInventory: false, 218 }, 219 wantDiff: []wantDiff{ 220 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Namespace", Name: "ns1"}, deleted: false}, // namespace should be deleted 221 {object: corev1.ObjectReference{APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition", Name: "crd1"}, deleted: false}, // crd should not be deleted 222 {object: corev1.ObjectReference{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration", Name: "mwh1"}, deleted: true}, // MutatingWebhookConfiguration should be deleted 223 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod1"}, deleted: true}, // provider components should be deleted 224 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod2"}, deleted: false}, // other objects in the namespace should not be deleted 225 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns2", Name: "pod3"}, deleted: false}, // this object is in another namespace, and should never be touched by delete 226 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "ns1-cluster-role"}, deleted: true}, // cluster-wide provider components should be deleted 227 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "some-cluster-role"}, deleted: false}, // other cluster-wide objects should be preserved 228 {object: corev1.ObjectReference{APIVersion: clusterctlv1.GroupVersion.String(), Kind: "Provider", Name: "providerOne"}, deleted: true}, // providerInventory should be deleted 229 }, 230 wantErr: false, 231 }, 232 { 233 name: "Delete provider, provider namespace and provider CRDs and the providerInventory", 234 args: args{ 235 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 236 includeNamespace: true, 237 includeCRD: true, 238 skipInventory: false, 239 }, 240 wantDiff: []wantDiff{ 241 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Namespace", Name: "ns1"}, deleted: true}, // namespace should be deleted 242 {object: corev1.ObjectReference{APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition", Name: "crd1"}, deleted: true}, // crd should be deleted 243 {object: corev1.ObjectReference{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration", Name: "mwh1"}, deleted: true}, // MutatingWebhookConfiguration should be deleted 244 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod1"}, deleted: true}, // provider components should be deleted 245 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns1", Name: "pod2"}, deleted: true}, // other objects in the namespace goes away when deleting the namespace 246 {object: corev1.ObjectReference{APIVersion: "v1", Kind: "Pod", Namespace: "ns2", Name: "pod3"}, deleted: false}, // this object is in another namespace, and should never be touched by delete 247 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "ns1-cluster-role"}, deleted: true}, // cluster-wide provider components should be deleted 248 {object: corev1.ObjectReference{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", Name: "some-cluster-role"}, deleted: false}, // other cluster-wide objects should be preserved 249 {object: corev1.ObjectReference{APIVersion: clusterctlv1.GroupVersion.String(), Kind: "Provider", Name: "providerOne"}, deleted: true}, // providerInventory should be deleted 250 }, 251 wantErr: false, 252 }, 253 } 254 for _, tt := range tests { 255 t.Run(tt.name, func(t *testing.T) { 256 g := NewWithT(t) 257 proxy := test.NewFakeProxy().WithObjs(initObjs...) 258 259 c := newComponentsClient(proxy) 260 261 err := c.Delete(context.Background(), DeleteOptions{ 262 Provider: tt.args.provider, 263 IncludeNamespace: tt.args.includeNamespace, 264 IncludeCRDs: tt.args.includeCRD, 265 SkipInventory: tt.args.skipInventory, 266 }) 267 if tt.wantErr { 268 g.Expect(err).To(HaveOccurred()) 269 return 270 } 271 272 g.Expect(err).ToNot(HaveOccurred()) 273 274 cs, err := proxy.NewClient(context.Background()) 275 g.Expect(err).ToNot(HaveOccurred()) 276 277 for _, want := range tt.wantDiff { 278 obj := &unstructured.Unstructured{} 279 obj.SetAPIVersion(want.object.APIVersion) 280 obj.SetKind(want.object.Kind) 281 282 key := client.ObjectKey{ 283 Namespace: want.object.Namespace, 284 Name: want.object.Name, 285 } 286 287 err := cs.Get(context.Background(), key, obj) 288 if err != nil && !apierrors.IsNotFound(err) { 289 t.Fatalf("Failed to get %v from the cluster: %v", key, err) 290 } 291 292 if !want.deleted && apierrors.IsNotFound(err) { 293 t.Errorf("%v deleted, expect NOT deleted", key) 294 } 295 296 if want.deleted && !apierrors.IsNotFound(err) { 297 if want.object.Namespace == tt.args.provider.Namespace && tt.args.includeNamespace { // Ignoring namespaced object that should be deleted by the namespace controller. 298 continue 299 } 300 t.Errorf("%v not deleted, expect deleted", key) 301 } 302 } 303 }) 304 } 305 } 306 307 func Test_providerComponents_DeleteCoreProviderWebhookNamespace(t *testing.T) { 308 t.Run("deletes capi-webhook-system namespace", func(t *testing.T) { 309 g := NewWithT(t) 310 labels := map[string]string{ 311 "foo": "bar", 312 } 313 initObjs := []client.Object{ 314 &corev1.Namespace{ 315 TypeMeta: metav1.TypeMeta{ 316 Kind: "Namespace", 317 }, 318 ObjectMeta: metav1.ObjectMeta{ 319 Name: "capi-webhook-system", 320 Labels: labels, 321 }, 322 }, 323 } 324 325 proxy := test.NewFakeProxy().WithObjs(initObjs...) 326 proxyClient, _ := proxy.NewClient(context.Background()) 327 var nsList corev1.NamespaceList 328 329 // assert length before deleting 330 _ = proxyClient.List(context.Background(), &nsList) 331 g.Expect(nsList.Items).Should(HaveLen(1)) 332 333 c := newComponentsClient(proxy) 334 err := c.DeleteWebhookNamespace(context.Background()) 335 g.Expect(err).To(Not(HaveOccurred())) 336 337 // assert length after deleting 338 _ = proxyClient.List(context.Background(), &nsList) 339 g.Expect(nsList.Items).Should(BeEmpty()) 340 }) 341 } 342 343 func Test_providerComponents_Create(t *testing.T) { 344 labelsOne := map[string]string{ 345 clusterv1.ProviderNameLabel: "infrastructure-infra", 346 } 347 commonObjects := []client.Object{ 348 // Namespace for the provider 349 &corev1.Namespace{ 350 TypeMeta: metav1.TypeMeta{ 351 Kind: "Namespace", 352 APIVersion: "v1", 353 }, 354 ObjectMeta: metav1.ObjectMeta{ 355 Name: "ns1", 356 Labels: labelsOne, 357 }, 358 }, 359 // A cluster-wide provider component. 360 &rbacv1.ClusterRole{ 361 TypeMeta: metav1.TypeMeta{ 362 Kind: "ClusterRole", 363 APIVersion: rbacv1.SchemeGroupVersion.WithKind("ClusterRole").GroupVersion().String(), 364 }, 365 ObjectMeta: metav1.ObjectMeta{ 366 Name: "ns1-cluster-role", // global objects belonging to the provider have a namespace prefix. 367 Labels: labelsOne, 368 }, 369 }, 370 } 371 // A namespaced provider component as a pod. 372 podOne := &corev1.Pod{ 373 TypeMeta: metav1.TypeMeta{ 374 Kind: "Pod", 375 APIVersion: "v1", 376 }, 377 ObjectMeta: metav1.ObjectMeta{ 378 Namespace: "ns1", 379 Name: "pod1", 380 Labels: labelsOne, 381 }, 382 Spec: corev1.PodSpec{ 383 Containers: []corev1.Container{ 384 { 385 Image: "pod1-v1", 386 }, 387 }, 388 }, 389 } 390 // podTwo is the same as podTwo but has an image titled pod1-v2. 391 podTwo := &corev1.Pod{ 392 TypeMeta: metav1.TypeMeta{ 393 Kind: "Pod", 394 APIVersion: "v1", 395 }, 396 ObjectMeta: metav1.ObjectMeta{ 397 Namespace: "ns1", 398 Name: "pod1", 399 Labels: labelsOne, 400 }, 401 Spec: corev1.PodSpec{ 402 Containers: []corev1.Container{ 403 { 404 Image: "pod1-v2", 405 }, 406 }, 407 }, 408 } 409 type args struct { 410 objectsToCreate []client.Object 411 initObjects []client.Object 412 } 413 414 tests := []struct { 415 name string 416 args args 417 want []client.Object 418 wantErr bool 419 }{ 420 { 421 name: "Create Provider Pod, Namespace and ClusterRole", 422 args: args{ 423 objectsToCreate: append(commonObjects, podOne), 424 initObjects: []client.Object{}, 425 }, 426 want: append(commonObjects, podOne), 427 wantErr: false, 428 }, 429 { 430 name: "Upgrade Provider Pod, Namespace and ClusterRole", 431 args: args{ 432 objectsToCreate: append(commonObjects, podTwo), 433 initObjects: append(commonObjects, podOne), 434 }, 435 want: append(commonObjects, podTwo), 436 wantErr: false, 437 }, 438 } 439 for _, tt := range tests { 440 t.Run(tt.name, func(t *testing.T) { 441 g := NewWithT(t) 442 443 proxy := test.NewFakeProxy().WithObjs(tt.args.initObjects...) 444 c := newComponentsClient(proxy) 445 var unstructuredObjectsToCreate []unstructured.Unstructured 446 for _, obj := range tt.args.objectsToCreate { 447 uns := &unstructured.Unstructured{} 448 if err := scheme.Scheme.Convert(obj, uns, nil); err != nil { 449 g.Expect(fmt.Errorf("%v %v could not be converted to unstructured", err.Error(), obj)).ToNot(HaveOccurred()) 450 } 451 unstructuredObjectsToCreate = append(unstructuredObjectsToCreate, *uns) 452 } 453 err := c.Create(context.Background(), unstructuredObjectsToCreate) 454 if tt.wantErr { 455 g.Expect(err).To(HaveOccurred()) 456 return 457 } 458 459 g.Expect(err).ToNot(HaveOccurred()) 460 461 cs, err := proxy.NewClient(context.Background()) 462 g.Expect(err).ToNot(HaveOccurred()) 463 464 for _, item := range tt.want { 465 obj := &unstructured.Unstructured{} 466 obj.SetKind(item.GetObjectKind().GroupVersionKind().Kind) 467 obj.SetAPIVersion(item.GetObjectKind().GroupVersionKind().GroupVersion().String()) 468 key := client.ObjectKey{ 469 Namespace: item.GetNamespace(), 470 Name: item.GetName(), 471 } 472 473 err := cs.Get(context.Background(), key, obj) 474 475 if err != nil && !apierrors.IsNotFound(err) { 476 t.Fatalf("Failed to get %v from the cluster: %v", key, err) 477 } 478 g.Expect(obj.GetNamespace()).To(Equal(item.GetNamespace()), cmp.Diff(obj.GetNamespace(), item.GetNamespace())) 479 g.Expect(obj.GetName()).To(Equal(item.GetName()), cmp.Diff(obj.GetName(), item.GetName())) 480 g.Expect(obj.GetAPIVersion()).To(Equal(item.GetObjectKind().GroupVersionKind().GroupVersion().String()), cmp.Diff(obj.GetAPIVersion(), item.GetObjectKind().GroupVersionKind().GroupVersion().String())) 481 if item.GetObjectKind().GroupVersionKind().Kind == "Pod" { 482 p1, okp1 := item.(*corev1.Pod) 483 if !(okp1) { 484 g.Expect(fmt.Errorf("%v %v could retrieve pod", err.Error(), obj)).ToNot(HaveOccurred()) 485 } 486 p2 := &corev1.Pod{} 487 if err := scheme.Scheme.Convert(obj, p2, nil); err != nil { 488 g.Expect(fmt.Errorf("%v %v could not be converted to unstructured", err.Error(), obj)).ToNot(HaveOccurred()) 489 } 490 if len(p1.Spec.Containers) == 0 || len(p2.Spec.Containers) == 0 { 491 g.Expect(fmt.Errorf("%v %v could not be converted to unstructured", err.Error(), obj)).ToNot(HaveOccurred()) 492 } 493 g.Expect(p1.Spec.Containers[0].Image).To(Equal(p2.Spec.Containers[0].Image), cmp.Diff(obj.GetNamespace(), item.GetNamespace())) 494 } 495 } 496 }) 497 } 498 } 499 500 func Test_providerComponents_ValidateNoObjectsExist(t *testing.T) { 501 labels := map[string]string{ 502 clusterv1.ProviderNameLabel: "infrastructure-infra", 503 } 504 505 crd := &apiextensionsv1.CustomResourceDefinition{ 506 TypeMeta: metav1.TypeMeta{ 507 Kind: "CustomResourceDefinition", 508 APIVersion: apiextensionsv1.SchemeGroupVersion.Identifier(), 509 }, 510 ObjectMeta: metav1.ObjectMeta{ 511 Name: "crd1", 512 Labels: labels, 513 }, 514 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 515 Group: "some.group", 516 Names: apiextensionsv1.CustomResourceDefinitionNames{ 517 ListKind: "SomeCRDList", 518 Kind: "SomeCRD", 519 }, 520 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 521 {Name: "v1", Storage: true}, 522 }, 523 }, 524 } 525 crd.ObjectMeta.Labels[clusterctlv1.ClusterctlLabel] = "" 526 527 cr := &unstructured.Unstructured{} 528 cr.SetAPIVersion("some.group/v1") 529 cr.SetKind("SomeCRD") 530 cr.SetName("cr1") 531 532 tests := []struct { 533 name string 534 provider clusterctlv1.Provider 535 initObjs []client.Object 536 wantErr bool 537 }{ 538 { 539 name: "No objects exist", 540 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 541 initObjs: []client.Object{}, 542 wantErr: false, 543 }, 544 { 545 name: "CRD exists but no objects", 546 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 547 initObjs: []client.Object{ 548 crd, 549 }, 550 wantErr: false, 551 }, 552 { 553 name: "CRD exists but and also objects", 554 provider: clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "infrastructure-infra", Namespace: "ns1"}, ProviderName: "infra", Type: string(clusterctlv1.InfrastructureProviderType)}, 555 initObjs: []client.Object{ 556 crd, 557 cr, 558 }, 559 wantErr: true, 560 }, 561 } 562 for _, tt := range tests { 563 t.Run(tt.name, func(t *testing.T) { 564 proxy := test.NewFakeProxy().WithObjs(tt.initObjs...) 565 566 c := newComponentsClient(proxy) 567 568 if err := c.ValidateNoObjectsExist(context.Background(), tt.provider); (err != nil) != tt.wantErr { 569 t.Errorf("providerComponents.ValidateNoObjectsExist() error = %v, wantErr %v", err, tt.wantErr) 570 } 571 }) 572 } 573 }