sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/repository/components_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 repository 18 19 import ( 20 "testing" 21 22 . "github.com/onsi/gomega" 23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 24 25 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 26 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" 27 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" 28 ) 29 30 func Test_inspectTargetNamespace(t *testing.T) { 31 type args struct { 32 objs []unstructured.Unstructured 33 } 34 tests := []struct { 35 name string 36 args args 37 want string 38 wantErr bool 39 }{ 40 { 41 name: "get targetNamespace if exists", 42 args: args{ 43 objs: []unstructured.Unstructured{ 44 { 45 Object: map[string]interface{}{ 46 "kind": "Namespace", 47 "metadata": map[string]interface{}{ 48 "name": "foo", 49 }, 50 }, 51 }, 52 }, 53 }, 54 want: "foo", 55 }, 56 { 57 name: "return empty if there is no targetNamespace", 58 args: args{ 59 objs: []unstructured.Unstructured{}, 60 }, 61 want: "", 62 }, 63 { 64 name: "fails if two Namespace exists", 65 args: args{ 66 objs: []unstructured.Unstructured{ 67 { 68 Object: map[string]interface{}{ 69 "kind": "Namespace", 70 "metadata": map[string]interface{}{ 71 "name": "foo", 72 }, 73 }, 74 }, 75 { 76 Object: map[string]interface{}{ 77 "kind": "Namespace", 78 "metadata": map[string]interface{}{ 79 "name": "bar", 80 }, 81 }, 82 }, 83 }, 84 }, 85 want: "", 86 wantErr: true, 87 }, 88 } 89 for _, tt := range tests { 90 t.Run(tt.name, func(t *testing.T) { 91 g := NewWithT(t) 92 93 got, err := inspectTargetNamespace(tt.args.objs) 94 if tt.wantErr { 95 g.Expect(err).To(HaveOccurred()) 96 return 97 } 98 g.Expect(err).ToNot(HaveOccurred()) 99 100 g.Expect(got).To(Equal(tt.want)) 101 }) 102 } 103 } 104 105 func Test_fixTargetNamespace(t *testing.T) { 106 tests := []struct { 107 name string 108 objs []unstructured.Unstructured 109 targetNamespace string 110 want []unstructured.Unstructured 111 wantErr bool 112 }{ 113 { 114 name: "fix Namespace object if exists", 115 objs: []unstructured.Unstructured{ 116 { 117 Object: map[string]interface{}{ 118 "apiVersion": "v1", 119 "kind": namespaceKind, 120 "metadata": map[string]interface{}{ 121 "name": "foo", 122 }, 123 }, 124 }, 125 }, 126 targetNamespace: "bar", 127 want: []unstructured.Unstructured{ 128 { 129 Object: map[string]interface{}{ 130 "apiVersion": "v1", 131 "kind": namespaceKind, 132 "metadata": map[string]interface{}{ 133 "name": "bar", 134 }, 135 }, 136 }, 137 }, 138 }, 139 { 140 name: "fix namespaced objects", 141 objs: []unstructured.Unstructured{ 142 { 143 Object: map[string]interface{}{ 144 "apiVersion": "apps/v1", 145 "kind": "Deployment", 146 "metadata": map[string]interface{}{ 147 "name": "test", 148 "namespace": "system", 149 }, 150 }, 151 }, 152 { 153 Object: map[string]interface{}{ 154 "apiVersion": "v1", 155 "kind": "Service", 156 "metadata": map[string]interface{}{ 157 "name": "capa-controller-manager-metrics-service", 158 "namespace": "capa-system", 159 }, 160 }, 161 }, 162 }, 163 targetNamespace: "bar", 164 want: []unstructured.Unstructured{ 165 { 166 Object: map[string]interface{}{ 167 "apiVersion": "apps/v1", 168 "kind": "Deployment", 169 "metadata": map[string]interface{}{ 170 "name": "test", 171 "namespace": "bar", 172 }, 173 }, 174 }, 175 { 176 Object: map[string]interface{}{ 177 "apiVersion": "v1", 178 "kind": "Service", 179 "metadata": map[string]interface{}{ 180 "name": "capa-controller-manager-metrics-service", 181 "namespace": "bar", 182 }, 183 }, 184 }, 185 }, 186 }, 187 { 188 name: "ignore global objects", 189 objs: []unstructured.Unstructured{ 190 { 191 Object: map[string]interface{}{ 192 "kind": "ClusterRole", 193 }, 194 }, 195 }, 196 targetNamespace: "bar", 197 want: []unstructured.Unstructured{ 198 { 199 Object: map[string]interface{}{ 200 "kind": "ClusterRole", 201 // no namespace 202 }, 203 }, 204 }, 205 }, 206 { 207 name: "fix v1beta1 webhook configs", 208 objs: []unstructured.Unstructured{ 209 { 210 Object: map[string]interface{}{ 211 "apiVersion": "admissionregistration.k8s.io/v1beta1", 212 "kind": "MutatingWebhookConfiguration", 213 "metadata": map[string]interface{}{ 214 "annotations": map[string]interface{}{ 215 "cert-manager.io/inject-ca-from": "capi-webhook-system/capm3-serving-cert", 216 }, 217 "name": "capm3-mutating-webhook-configuration", 218 }, 219 "webhooks": []interface{}{ 220 map[string]interface{}{ 221 "clientConfig": map[string]interface{}{ 222 "caBundle": "Cg==", 223 "service": map[string]interface{}{ 224 "name": "capm3-webhook-service", 225 "namespace": "capi-webhook-system", 226 "path": "/mutate-infrastructure-cluster-x-k8s-io-v1alpha4-metal3cluster", 227 }, 228 }, 229 }, 230 }, 231 }, 232 }, 233 }, 234 targetNamespace: "bar", 235 want: []unstructured.Unstructured{ 236 { 237 Object: map[string]interface{}{ 238 "apiVersion": "admissionregistration.k8s.io/v1beta1", 239 "kind": "MutatingWebhookConfiguration", 240 "metadata": map[string]interface{}{ 241 "annotations": map[string]interface{}{ 242 "cert-manager.io/inject-ca-from": "bar/capm3-serving-cert", 243 }, 244 "creationTimestamp": nil, 245 "name": "capm3-mutating-webhook-configuration", 246 }, 247 "webhooks": []interface{}{ 248 map[string]interface{}{ 249 "name": "", 250 "clientConfig": map[string]interface{}{ 251 "service": map[string]interface{}{ 252 "name": "capm3-webhook-service", 253 "path": "/mutate-infrastructure-cluster-x-k8s-io-v1alpha4-metal3cluster", 254 "namespace": "bar", 255 }, 256 "caBundle": "Cg==", 257 }, 258 }, 259 }, 260 }, 261 }, 262 }, 263 }, 264 { 265 name: "unable to fix v1beta2 webhook configs", 266 objs: []unstructured.Unstructured{ 267 { 268 Object: map[string]interface{}{ 269 "apiVersion": "admissionregistration.k8s.io/v1beta2", 270 "kind": "MutatingWebhookConfiguration", 271 "metadata": map[string]interface{}{ 272 "annotations": map[string]interface{}{ 273 "cert-manager.io/inject-ca-from": "capi-webhook-system/capm3-serving-cert", 274 }, 275 "name": "capm3-mutating-webhook-configuration", 276 }, 277 "webhooks": []interface{}{ 278 map[string]interface{}{ 279 "clientConfig": map[string]interface{}{ 280 "caBundle": "Cg==", 281 "service": map[string]interface{}{ 282 "name": "capm3-webhook-service", 283 "namespace": "capi-webhook-system", 284 "path": "/mutate-infrastructure-cluster-x-k8s-io-v1alpha4-metal3cluster", 285 }, 286 }, 287 }, 288 }, 289 }, 290 }, 291 }, 292 targetNamespace: "bar", 293 wantErr: true, 294 }, { 295 name: "fix v1 webhook configs", 296 objs: []unstructured.Unstructured{ 297 { 298 Object: map[string]interface{}{ 299 "apiVersion": "admissionregistration.k8s.io/v1", 300 "kind": "MutatingWebhookConfiguration", 301 "metadata": map[string]interface{}{ 302 "annotations": map[string]interface{}{ 303 "cert-manager.io/inject-ca-from": "capi-webhook-system/capm3-serving-cert", 304 }, 305 "name": "capm3-mutating-webhook-configuration", 306 }, 307 "webhooks": []interface{}{ 308 map[string]interface{}{ 309 "clientConfig": map[string]interface{}{ 310 "caBundle": "Cg==", 311 "service": map[string]interface{}{ 312 "name": "capm3-webhook-service", 313 "namespace": "capi-webhook-system", 314 "path": "/mutate-infrastructure-cluster-x-k8s-io-v1alpha4-metal3cluster", 315 }, 316 }, 317 }, 318 }, 319 }, 320 }, 321 }, 322 targetNamespace: "bar", 323 want: []unstructured.Unstructured{ 324 { 325 Object: map[string]interface{}{ 326 "apiVersion": "admissionregistration.k8s.io/v1", 327 "kind": "MutatingWebhookConfiguration", 328 "metadata": map[string]interface{}{ 329 "annotations": map[string]interface{}{ 330 "cert-manager.io/inject-ca-from": "bar/capm3-serving-cert", 331 }, 332 "creationTimestamp": nil, 333 "name": "capm3-mutating-webhook-configuration", 334 }, 335 "webhooks": []interface{}{ 336 map[string]interface{}{ 337 "name": "", 338 "admissionReviewVersions": nil, 339 "clientConfig": map[string]interface{}{ 340 "service": map[string]interface{}{ 341 "name": "capm3-webhook-service", 342 "path": "/mutate-infrastructure-cluster-x-k8s-io-v1alpha4-metal3cluster", 343 "namespace": "bar", 344 }, 345 "caBundle": "Cg==", 346 }, 347 "sideEffects": nil, 348 }, 349 }, 350 }, 351 }, 352 }, 353 }, 354 { 355 name: "fix v1beta1 crd webhook namespace", 356 objs: []unstructured.Unstructured{ 357 { 358 Object: map[string]interface{}{ 359 "apiVersion": "apiextensions.k8s.io/v1beta1", 360 "kind": "CustomResourceDefinition", 361 "metadata": map[string]interface{}{ 362 "annotations": map[string]interface{}{ 363 "cert-manager.io/inject-ca-from": "capi-webhook-system/capm3-serving-cert", 364 }, 365 "name": "aCoolName", 366 }, 367 "spec": map[string]interface{}{ 368 "conversion": map[string]interface{}{ 369 "strategy": "Webhook", 370 "webhookClientConfig": map[string]interface{}{ 371 "caBundle": "Cg==", 372 "service": map[string]interface{}{ 373 "name": "capa-webhook-service", 374 "namespace": "capi-webhook-system", 375 "path": "/convert", 376 }, 377 }, 378 }, 379 }, 380 }, 381 }, 382 }, 383 targetNamespace: "bar", 384 want: []unstructured.Unstructured{ 385 { 386 Object: map[string]interface{}{ 387 "apiVersion": "apiextensions.k8s.io/v1beta1", 388 "kind": "CustomResourceDefinition", 389 "metadata": map[string]interface{}{ 390 "annotations": map[string]interface{}{ 391 "cert-manager.io/inject-ca-from": "bar/capm3-serving-cert", 392 }, 393 "creationTimestamp": nil, 394 "name": "aCoolName", 395 }, 396 "spec": map[string]interface{}{ 397 "group": "", 398 "names": map[string]interface{}{"plural": "", "kind": ""}, 399 "scope": "", 400 "conversion": map[string]interface{}{ 401 "strategy": "Webhook", 402 "webhookClientConfig": map[string]interface{}{ 403 "caBundle": "Cg==", 404 "service": map[string]interface{}{ 405 "name": "capa-webhook-service", 406 "namespace": "bar", 407 "path": "/convert", 408 }, 409 }, 410 }, 411 }, 412 "status": map[string]interface{}{ 413 "storedVersions": nil, 414 "conditions": nil, 415 "acceptedNames": map[string]interface{}{"kind": "", "plural": ""}, 416 }, 417 }, 418 }, 419 }, 420 }, 421 { 422 name: "unable to fix v1beta2 crd webhook namespace", 423 objs: []unstructured.Unstructured{ 424 { 425 Object: map[string]interface{}{ 426 "apiVersion": "apiextensions.k8s.io/v1beta2", 427 "kind": "CustomResourceDefinition", 428 "metadata": map[string]interface{}{ 429 "annotations": map[string]interface{}{ 430 "cert-manager.io/inject-ca-from": "capi-webhook-system/capm3-serving-cert", 431 }, 432 "name": "aCoolName", 433 }, 434 "spec": map[string]interface{}{ 435 "conversion": map[string]interface{}{ 436 "strategy": "Webhook", 437 "webhookClientConfig": map[string]interface{}{ 438 "caBundle": "Cg==", 439 "service": map[string]interface{}{ 440 "name": "capa-webhook-service", 441 "namespace": "capi-webhook-system", 442 "path": "/convert", 443 }, 444 }, 445 }, 446 }, 447 }, 448 }, 449 }, 450 targetNamespace: "bar", 451 wantErr: true, 452 }, 453 { 454 name: "fix v1 crd webhook namespace", 455 objs: []unstructured.Unstructured{ 456 { 457 Object: map[string]interface{}{ 458 "apiVersion": "apiextensions.k8s.io/v1", 459 "kind": "CustomResourceDefinition", 460 "metadata": map[string]interface{}{ 461 "annotations": map[string]interface{}{ 462 "cert-manager.io/inject-ca-from": "capi-webhook-system/capm3-serving-cert", 463 }, 464 "name": "aCoolName", 465 }, 466 "spec": map[string]interface{}{ 467 "conversion": map[string]interface{}{ 468 "strategy": "Webhook", 469 "webhook": map[string]interface{}{ 470 "clientConfig": map[string]interface{}{ 471 "caBundle": "Cg==", 472 "service": map[string]interface{}{ 473 "name": "capa-webhook-service", 474 "namespace": "capi-webhook-system", 475 "path": "/convert", 476 }, 477 }, 478 }, 479 }, 480 }, 481 }, 482 }, 483 }, 484 targetNamespace: "bar", 485 want: []unstructured.Unstructured{ 486 { 487 Object: map[string]interface{}{ 488 "apiVersion": "apiextensions.k8s.io/v1", 489 "kind": "CustomResourceDefinition", 490 "metadata": map[string]interface{}{ 491 "annotations": map[string]interface{}{ 492 "cert-manager.io/inject-ca-from": "bar/capm3-serving-cert", 493 }, 494 "creationTimestamp": nil, 495 "name": "aCoolName", 496 }, 497 "spec": map[string]interface{}{ 498 "group": "", 499 "names": map[string]interface{}{"plural": "", "kind": ""}, 500 "scope": "", 501 "versions": nil, 502 "conversion": map[string]interface{}{ 503 "strategy": "Webhook", 504 "webhook": map[string]interface{}{ 505 "conversionReviewVersions": nil, 506 "clientConfig": map[string]interface{}{ 507 "caBundle": "Cg==", 508 "service": map[string]interface{}{ 509 "name": "capa-webhook-service", 510 "namespace": "bar", 511 "path": "/convert", 512 }, 513 }, 514 }, 515 }, 516 }, 517 "status": map[string]interface{}{ 518 "storedVersions": nil, 519 "conditions": nil, 520 "acceptedNames": map[string]interface{}{"kind": "", "plural": ""}, 521 }, 522 }, 523 }, 524 }, 525 }, 526 { 527 name: "fix cert-manager Certificate", 528 objs: []unstructured.Unstructured{ 529 { 530 Object: map[string]interface{}{ 531 "apiVersion": "cert-manager.io/v1", 532 "kind": "Certificate", 533 "metadata": map[string]interface{}{ 534 "name": "capi-serving-cert", 535 "namespace": "capi-system", 536 }, 537 "spec": map[string]interface{}{ 538 "dnsNames": []interface{}{ 539 "capi-webhook-service.capi-system.svc", 540 "capi-webhook-service.capi-system.svc.cluster.local", 541 "random-other-dns-name", 542 }, 543 "issuerRef": map[string]interface{}{ 544 "kind": "Issuer", 545 "name": "capi-selfsigned-issuer", 546 }, 547 "secretName": "capi-webhook-service-cert", 548 }, 549 }, 550 }, 551 }, 552 targetNamespace: "bar", 553 want: []unstructured.Unstructured{ 554 { 555 Object: map[string]interface{}{ 556 "apiVersion": "cert-manager.io/v1", 557 "kind": "Certificate", 558 "metadata": map[string]interface{}{ 559 "name": "capi-serving-cert", 560 "namespace": "bar", 561 }, 562 "spec": map[string]interface{}{ 563 "dnsNames": []interface{}{ 564 "capi-webhook-service.bar.svc", 565 "capi-webhook-service.bar.svc.cluster.local", 566 "random-other-dns-name", 567 }, 568 "issuerRef": map[string]interface{}{ 569 "kind": "Issuer", 570 "name": "capi-selfsigned-issuer", 571 }, 572 "secretName": "capi-webhook-service-cert", 573 }, 574 }, 575 }, 576 }, 577 }, 578 } 579 for _, tt := range tests { 580 t.Run(tt.name, func(t *testing.T) { 581 g := NewWithT(t) 582 583 got, err := fixTargetNamespace(tt.objs, tt.targetNamespace) 584 if tt.wantErr { 585 g.Expect(err).To(HaveOccurred()) 586 return 587 } 588 589 g.Expect(err).ToNot(HaveOccurred()) 590 g.Expect(got).To(ContainElements(tt.want)) // skipping from test the automatically added namespace Object 591 }) 592 } 593 } 594 595 func Test_addNamespaceIfMissing(t *testing.T) { 596 type args struct { 597 objs []unstructured.Unstructured 598 targetNamespace string 599 } 600 tests := []struct { 601 name string 602 args args 603 }{ 604 { 605 name: "don't add Namespace object if exists", 606 args: args{ 607 objs: []unstructured.Unstructured{ 608 { 609 Object: map[string]interface{}{ 610 "kind": namespaceKind, 611 "metadata": map[string]interface{}{ 612 "name": "foo", 613 }, 614 }, 615 }, 616 }, 617 targetNamespace: "foo", 618 }, 619 }, 620 { 621 name: "add Namespace object if it does not exists", 622 args: args{ 623 objs: []unstructured.Unstructured{}, 624 targetNamespace: "bar", 625 }, 626 }, 627 } 628 for _, tt := range tests { 629 t.Run(tt.name, func(t *testing.T) { 630 g := NewWithT(t) 631 632 got := addNamespaceIfMissing(tt.args.objs, tt.args.targetNamespace) 633 634 wgot, err := inspectTargetNamespace(got) 635 g.Expect(err).ToNot(HaveOccurred()) 636 g.Expect(wgot).To(Equal(tt.args.targetNamespace)) 637 }) 638 } 639 } 640 641 func Test_addCommonLabels(t *testing.T) { 642 type args struct { 643 objs []unstructured.Unstructured 644 name string 645 providerType clusterctlv1.ProviderType 646 } 647 tests := []struct { 648 name string 649 args args 650 want []unstructured.Unstructured 651 }{ 652 { 653 name: "add labels", 654 args: args{ 655 objs: []unstructured.Unstructured{ 656 { 657 Object: map[string]interface{}{ 658 "kind": "ClusterRole", 659 }, 660 }, 661 }, 662 name: "provider", 663 providerType: clusterctlv1.InfrastructureProviderType, 664 }, 665 want: []unstructured.Unstructured{ 666 { 667 Object: map[string]interface{}{ 668 "kind": "ClusterRole", 669 "metadata": map[string]interface{}{ 670 "labels": map[string]interface{}{ 671 clusterctlv1.ClusterctlLabel: "", 672 clusterv1.ProviderNameLabel: "infrastructure-provider", 673 }, 674 }, 675 }, 676 }, 677 }, 678 }, 679 } 680 for _, tt := range tests { 681 t.Run(tt.name, func(t *testing.T) { 682 g := NewWithT(t) 683 684 got := addCommonLabels(tt.args.objs, config.NewProvider(tt.args.name, "", tt.args.providerType)) 685 g.Expect(got).To(BeComparableTo(tt.want)) 686 }) 687 } 688 } 689 690 func TestAlterComponents(t *testing.T) { 691 c := &components{ 692 targetNamespace: "test-ns", 693 objs: []unstructured.Unstructured{ 694 { 695 Object: map[string]interface{}{ 696 "kind": "ClusterRole", 697 }, 698 }, 699 }, 700 } 701 want := []unstructured.Unstructured{ 702 { 703 Object: map[string]interface{}{ 704 "kind": "ClusterRole", 705 "metadata": map[string]interface{}{ 706 "labels": map[string]interface{}{ 707 clusterctlv1.ClusterctlLabel: "", 708 clusterv1.ProviderNameLabel: "infrastructure-provider", 709 }, 710 }, 711 }, 712 }, 713 } 714 715 alterFn := func(objs []unstructured.Unstructured) ([]unstructured.Unstructured, error) { 716 // reusing addCommonLabels to do an example modification. 717 return addCommonLabels(objs, config.NewProvider("provider", "", clusterctlv1.InfrastructureProviderType)), nil 718 } 719 720 g := NewWithT(t) 721 if err := AlterComponents(c, alterFn); err != nil { 722 t.Errorf("AlterComponents() error = %v", err) 723 } 724 g.Expect(c.objs).To(BeComparableTo(want)) 725 }