sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azureclustertemplate_default_test.go (about) 1 /* 2 Copyright 2022 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 v1beta1 18 19 import ( 20 "encoding/json" 21 "reflect" 22 "testing" 23 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/utils/ptr" 26 ) 27 28 func TestVnetTemplateDefaults(t *testing.T) { 29 cases := []struct { 30 name string 31 clusterTemplate *AzureClusterTemplate 32 outputTemplate *AzureClusterTemplate 33 }{ 34 { 35 name: "vnet not specified", 36 clusterTemplate: &AzureClusterTemplate{ 37 ObjectMeta: metav1.ObjectMeta{ 38 Name: "test-cluster-template", 39 }, 40 Spec: AzureClusterTemplateSpec{ 41 Template: AzureClusterTemplateResource{}, 42 }, 43 }, 44 outputTemplate: &AzureClusterTemplate{ 45 ObjectMeta: metav1.ObjectMeta{ 46 Name: "test-cluster-template", 47 }, 48 Spec: AzureClusterTemplateSpec{ 49 Template: AzureClusterTemplateResource{ 50 Spec: AzureClusterTemplateResourceSpec{ 51 NetworkSpec: NetworkTemplateSpec{ 52 Vnet: VnetTemplateSpec{ 53 VnetClassSpec: VnetClassSpec{ 54 CIDRBlocks: []string{DefaultVnetCIDR}, 55 }, 56 }, 57 }, 58 }, 59 }, 60 }, 61 }, 62 }, 63 { 64 name: "custom CIDR", 65 clusterTemplate: &AzureClusterTemplate{ 66 ObjectMeta: metav1.ObjectMeta{ 67 Name: "test-cluster-template", 68 }, 69 Spec: AzureClusterTemplateSpec{ 70 Template: AzureClusterTemplateResource{ 71 Spec: AzureClusterTemplateResourceSpec{ 72 NetworkSpec: NetworkTemplateSpec{ 73 Vnet: VnetTemplateSpec{ 74 VnetClassSpec: VnetClassSpec{ 75 CIDRBlocks: []string{"10.0.0.0/16"}, 76 }, 77 }, 78 }, 79 }, 80 }, 81 }, 82 }, 83 outputTemplate: &AzureClusterTemplate{ 84 ObjectMeta: metav1.ObjectMeta{ 85 Name: "test-cluster-template", 86 }, 87 Spec: AzureClusterTemplateSpec{ 88 Template: AzureClusterTemplateResource{ 89 Spec: AzureClusterTemplateResourceSpec{ 90 NetworkSpec: NetworkTemplateSpec{ 91 Vnet: VnetTemplateSpec{ 92 VnetClassSpec: VnetClassSpec{ 93 CIDRBlocks: []string{"10.0.0.0/16"}, 94 }, 95 }, 96 }, 97 }, 98 }, 99 }, 100 }, 101 }, 102 { 103 name: "IPv6 enabled", 104 clusterTemplate: &AzureClusterTemplate{ 105 ObjectMeta: metav1.ObjectMeta{ 106 Name: "test-cluster-template", 107 }, 108 Spec: AzureClusterTemplateSpec{ 109 Template: AzureClusterTemplateResource{ 110 Spec: AzureClusterTemplateResourceSpec{ 111 NetworkSpec: NetworkTemplateSpec{ 112 Vnet: VnetTemplateSpec{ 113 VnetClassSpec: VnetClassSpec{ 114 CIDRBlocks: []string{"2001:1234:5678:9a00::/56"}, 115 }, 116 }, 117 }, 118 }, 119 }, 120 }, 121 }, 122 outputTemplate: &AzureClusterTemplate{ 123 ObjectMeta: metav1.ObjectMeta{ 124 Name: "test-cluster-template", 125 }, 126 Spec: AzureClusterTemplateSpec{ 127 Template: AzureClusterTemplateResource{ 128 Spec: AzureClusterTemplateResourceSpec{ 129 NetworkSpec: NetworkTemplateSpec{ 130 Vnet: VnetTemplateSpec{ 131 VnetClassSpec: VnetClassSpec{ 132 CIDRBlocks: []string{"2001:1234:5678:9a00::/56"}, 133 }, 134 }, 135 }, 136 }, 137 }, 138 }, 139 }, 140 }, 141 } 142 143 for _, c := range cases { 144 tc := c 145 t.Run(tc.name, func(t *testing.T) { 146 t.Parallel() 147 tc.clusterTemplate.setVnetTemplateDefaults() 148 if !reflect.DeepEqual(tc.clusterTemplate, tc.outputTemplate) { 149 expected, _ := json.MarshalIndent(tc.outputTemplate, "", "\t") 150 actual, _ := json.MarshalIndent(tc.clusterTemplate, "", "\t") 151 t.Errorf("Expected %s, got %s", string(expected), string(actual)) 152 } 153 }) 154 } 155 } 156 157 func TestSubnetsTemplateDefaults(t *testing.T) { 158 cases := []struct { 159 name string 160 clusterTemplate *AzureClusterTemplate 161 outputTemplate *AzureClusterTemplate 162 }{ 163 { 164 name: "no subnets", 165 clusterTemplate: &AzureClusterTemplate{ 166 ObjectMeta: metav1.ObjectMeta{ 167 Name: "test-cluster-template", 168 }, 169 Spec: AzureClusterTemplateSpec{ 170 Template: AzureClusterTemplateResource{ 171 Spec: AzureClusterTemplateResourceSpec{ 172 NetworkSpec: NetworkTemplateSpec{}, 173 }, 174 }, 175 }, 176 }, 177 outputTemplate: &AzureClusterTemplate{ 178 ObjectMeta: metav1.ObjectMeta{ 179 Name: "test-cluster-template", 180 }, 181 Spec: AzureClusterTemplateSpec{ 182 Template: AzureClusterTemplateResource{ 183 Spec: AzureClusterTemplateResourceSpec{ 184 NetworkSpec: NetworkTemplateSpec{ 185 Subnets: SubnetTemplatesSpec{ 186 { 187 SubnetClassSpec: SubnetClassSpec{ 188 Role: SubnetControlPlane, 189 CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR}, 190 }, 191 }, 192 { 193 SubnetClassSpec: SubnetClassSpec{ 194 Role: SubnetNode, 195 CIDRBlocks: []string{DefaultNodeSubnetCIDR}, 196 }, 197 }, 198 }, 199 }, 200 }, 201 }, 202 }, 203 }, 204 }, 205 { 206 name: "subnet with custom attributes", 207 clusterTemplate: &AzureClusterTemplate{ 208 ObjectMeta: metav1.ObjectMeta{ 209 Name: "test-cluster-template", 210 }, 211 Spec: AzureClusterTemplateSpec{ 212 Template: AzureClusterTemplateResource{ 213 Spec: AzureClusterTemplateResourceSpec{ 214 NetworkSpec: NetworkTemplateSpec{ 215 Subnets: SubnetTemplatesSpec{ 216 { 217 SubnetClassSpec: SubnetClassSpec{ 218 Role: SubnetControlPlane, 219 CIDRBlocks: []string{"10.0.0.16/24"}, 220 }, 221 }, 222 { 223 SubnetClassSpec: SubnetClassSpec{ 224 Role: SubnetNode, 225 CIDRBlocks: []string{"10.1.0.16/24"}, 226 }, 227 NatGateway: NatGatewayClassSpec{ 228 Name: "foo-natgw", 229 }, 230 }, 231 }, 232 }, 233 }, 234 }, 235 }, 236 }, 237 outputTemplate: &AzureClusterTemplate{ 238 ObjectMeta: metav1.ObjectMeta{ 239 Name: "test-cluster-template", 240 }, 241 Spec: AzureClusterTemplateSpec{ 242 Template: AzureClusterTemplateResource{ 243 Spec: AzureClusterTemplateResourceSpec{ 244 NetworkSpec: NetworkTemplateSpec{ 245 Subnets: SubnetTemplatesSpec{ 246 { 247 SubnetClassSpec: SubnetClassSpec{ 248 Role: SubnetControlPlane, 249 CIDRBlocks: []string{"10.0.0.16/24"}, 250 }, 251 }, 252 { 253 SubnetClassSpec: SubnetClassSpec{ 254 Role: SubnetNode, 255 CIDRBlocks: []string{"10.1.0.16/24"}, 256 }, 257 NatGateway: NatGatewayClassSpec{Name: "foo-natgw"}, 258 }, 259 }, 260 }, 261 }, 262 }, 263 }, 264 }, 265 }, 266 { 267 name: "subnet with custom attributes and security groups", 268 clusterTemplate: &AzureClusterTemplate{ 269 ObjectMeta: metav1.ObjectMeta{ 270 Name: "test-cluster-template", 271 }, 272 Spec: AzureClusterTemplateSpec{ 273 Template: AzureClusterTemplateResource{ 274 Spec: AzureClusterTemplateResourceSpec{ 275 NetworkSpec: NetworkTemplateSpec{ 276 Subnets: SubnetTemplatesSpec{ 277 { 278 SubnetClassSpec: SubnetClassSpec{ 279 Role: SubnetControlPlane, 280 CIDRBlocks: []string{"10.0.0.16/24"}, 281 }, 282 SecurityGroup: SecurityGroupClass{ 283 SecurityRules: []SecurityRule{ 284 { 285 Name: "allow_port_50000", 286 Description: "allow port 50000", 287 Protocol: "*", 288 Priority: 2202, 289 SourcePorts: ptr.To("*"), 290 DestinationPorts: ptr.To("*"), 291 Source: ptr.To("*"), 292 Destination: ptr.To("*"), 293 Action: SecurityRuleActionAllow, 294 }, 295 }, 296 }, 297 }, 298 { 299 SubnetClassSpec: SubnetClassSpec{ 300 Role: SubnetNode, 301 CIDRBlocks: []string{"10.1.0.16/24"}, 302 }, 303 NatGateway: NatGatewayClassSpec{ 304 Name: "foo-natgw", 305 }, 306 SecurityGroup: SecurityGroupClass{ 307 SecurityRules: []SecurityRule{ 308 { 309 Name: "allow_port_50000", 310 Description: "allow port 50000", 311 Protocol: "*", 312 Priority: 2202, 313 SourcePorts: ptr.To("*"), 314 DestinationPorts: ptr.To("*"), 315 Source: ptr.To("*"), 316 Destination: ptr.To("*"), 317 Action: SecurityRuleActionAllow, 318 }, 319 }, 320 }, 321 }, 322 }, 323 }, 324 }, 325 }, 326 }, 327 }, 328 outputTemplate: &AzureClusterTemplate{ 329 ObjectMeta: metav1.ObjectMeta{ 330 Name: "test-cluster-template", 331 }, 332 Spec: AzureClusterTemplateSpec{ 333 Template: AzureClusterTemplateResource{ 334 Spec: AzureClusterTemplateResourceSpec{ 335 NetworkSpec: NetworkTemplateSpec{ 336 Subnets: SubnetTemplatesSpec{ 337 { 338 SubnetClassSpec: SubnetClassSpec{ 339 Role: SubnetControlPlane, 340 CIDRBlocks: []string{"10.0.0.16/24"}, 341 }, 342 SecurityGroup: SecurityGroupClass{ 343 SecurityRules: SecurityRules{ 344 { 345 Name: "allow_port_50000", 346 Description: "allow port 50000", 347 Protocol: "*", 348 Priority: 2202, 349 SourcePorts: ptr.To("*"), 350 DestinationPorts: ptr.To("*"), 351 Source: ptr.To("*"), 352 Destination: ptr.To("*"), 353 Direction: SecurityRuleDirectionInbound, 354 Action: SecurityRuleActionAllow, 355 }, 356 }, 357 }, 358 }, 359 { 360 SubnetClassSpec: SubnetClassSpec{ 361 Role: SubnetNode, 362 CIDRBlocks: []string{"10.1.0.16/24"}, 363 }, 364 NatGateway: NatGatewayClassSpec{Name: "foo-natgw"}, 365 SecurityGroup: SecurityGroupClass{ 366 SecurityRules: SecurityRules{ 367 { 368 Name: "allow_port_50000", 369 Description: "allow port 50000", 370 Protocol: "*", 371 Priority: 2202, 372 SourcePorts: ptr.To("*"), 373 DestinationPorts: ptr.To("*"), 374 Source: ptr.To("*"), 375 Destination: ptr.To("*"), 376 Direction: SecurityRuleDirectionInbound, 377 Action: SecurityRuleActionAllow, 378 }, 379 }, 380 }, 381 }, 382 }, 383 }, 384 }, 385 }, 386 }, 387 }, 388 }, 389 { 390 name: "subnets specified", 391 clusterTemplate: &AzureClusterTemplate{ 392 ObjectMeta: metav1.ObjectMeta{ 393 Name: "test-cluster-template", 394 }, 395 Spec: AzureClusterTemplateSpec{ 396 Template: AzureClusterTemplateResource{ 397 Spec: AzureClusterTemplateResourceSpec{ 398 NetworkSpec: NetworkTemplateSpec{ 399 Subnets: SubnetTemplatesSpec{ 400 { 401 SubnetClassSpec: SubnetClassSpec{ 402 Role: SubnetControlPlane, 403 }, 404 }, 405 { 406 SubnetClassSpec: SubnetClassSpec{ 407 Role: SubnetNode, 408 }, 409 }, 410 }, 411 }, 412 }, 413 }, 414 }, 415 }, 416 outputTemplate: &AzureClusterTemplate{ 417 ObjectMeta: metav1.ObjectMeta{ 418 Name: "test-cluster-template", 419 }, 420 Spec: AzureClusterTemplateSpec{ 421 Template: AzureClusterTemplateResource{ 422 Spec: AzureClusterTemplateResourceSpec{ 423 NetworkSpec: NetworkTemplateSpec{ 424 Subnets: SubnetTemplatesSpec{ 425 { 426 SubnetClassSpec: SubnetClassSpec{ 427 Role: SubnetControlPlane, 428 CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR}, 429 }, 430 }, 431 { 432 SubnetClassSpec: SubnetClassSpec{ 433 Role: SubnetNode, 434 CIDRBlocks: []string{DefaultNodeSubnetCIDR}, 435 }, 436 }, 437 }, 438 }, 439 }, 440 }, 441 }, 442 }, 443 }, 444 { 445 name: "cluster subnet with custom attributes", 446 clusterTemplate: &AzureClusterTemplate{ 447 ObjectMeta: metav1.ObjectMeta{ 448 Name: "test-cluster-template", 449 }, 450 Spec: AzureClusterTemplateSpec{ 451 Template: AzureClusterTemplateResource{ 452 Spec: AzureClusterTemplateResourceSpec{ 453 NetworkSpec: NetworkTemplateSpec{ 454 Subnets: SubnetTemplatesSpec{ 455 { 456 SubnetClassSpec: SubnetClassSpec{ 457 Role: SubnetCluster, 458 CIDRBlocks: []string{"10.1.0.16/24"}, 459 }, 460 NatGateway: NatGatewayClassSpec{ 461 Name: "foo-natgw", 462 }, 463 }, 464 }, 465 }, 466 }, 467 }, 468 }, 469 }, 470 outputTemplate: &AzureClusterTemplate{ 471 ObjectMeta: metav1.ObjectMeta{ 472 Name: "test-cluster-template", 473 }, 474 Spec: AzureClusterTemplateSpec{ 475 Template: AzureClusterTemplateResource{ 476 Spec: AzureClusterTemplateResourceSpec{ 477 NetworkSpec: NetworkTemplateSpec{ 478 Subnets: SubnetTemplatesSpec{ 479 { 480 SubnetClassSpec: SubnetClassSpec{ 481 Role: SubnetCluster, 482 CIDRBlocks: []string{"10.1.0.16/24"}, 483 }, 484 NatGateway: NatGatewayClassSpec{Name: "foo-natgw"}, 485 }, 486 }, 487 }, 488 }, 489 }, 490 }, 491 }, 492 }, 493 { 494 name: "subnets specified", 495 clusterTemplate: &AzureClusterTemplate{ 496 ObjectMeta: metav1.ObjectMeta{ 497 Name: "test-cluster-template", 498 }, 499 Spec: AzureClusterTemplateSpec{ 500 Template: AzureClusterTemplateResource{ 501 Spec: AzureClusterTemplateResourceSpec{ 502 NetworkSpec: NetworkTemplateSpec{ 503 Subnets: SubnetTemplatesSpec{ 504 { 505 SubnetClassSpec: SubnetClassSpec{ 506 Role: SubnetCluster, 507 }, 508 }, 509 }, 510 }, 511 }, 512 }, 513 }, 514 }, 515 outputTemplate: &AzureClusterTemplate{ 516 ObjectMeta: metav1.ObjectMeta{ 517 Name: "test-cluster-template", 518 }, 519 Spec: AzureClusterTemplateSpec{ 520 Template: AzureClusterTemplateResource{ 521 Spec: AzureClusterTemplateResourceSpec{ 522 NetworkSpec: NetworkTemplateSpec{ 523 Subnets: SubnetTemplatesSpec{ 524 { 525 SubnetClassSpec: SubnetClassSpec{ 526 Role: SubnetCluster, 527 CIDRBlocks: []string{DefaultClusterSubnetCIDR}, 528 }, 529 }, 530 }, 531 }, 532 }, 533 }, 534 }, 535 }, 536 }, 537 { 538 name: "only node subnet specified", 539 clusterTemplate: &AzureClusterTemplate{ 540 ObjectMeta: metav1.ObjectMeta{ 541 Name: "test-cluster-template", 542 }, 543 Spec: AzureClusterTemplateSpec{ 544 Template: AzureClusterTemplateResource{ 545 Spec: AzureClusterTemplateResourceSpec{ 546 NetworkSpec: NetworkTemplateSpec{ 547 Subnets: SubnetTemplatesSpec{ 548 { 549 SubnetClassSpec: SubnetClassSpec{ 550 Role: SubnetNode, 551 }, 552 }, 553 }, 554 }, 555 }, 556 }, 557 }, 558 }, 559 outputTemplate: &AzureClusterTemplate{ 560 ObjectMeta: metav1.ObjectMeta{ 561 Name: "test-cluster-template", 562 }, 563 Spec: AzureClusterTemplateSpec{ 564 Template: AzureClusterTemplateResource{ 565 Spec: AzureClusterTemplateResourceSpec{ 566 NetworkSpec: NetworkTemplateSpec{ 567 Subnets: SubnetTemplatesSpec{ 568 { 569 SubnetClassSpec: SubnetClassSpec{ 570 Role: SubnetNode, 571 CIDRBlocks: []string{DefaultNodeSubnetCIDR}, 572 }, 573 }, 574 { 575 SubnetClassSpec: SubnetClassSpec{ 576 Role: SubnetControlPlane, 577 CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR}, 578 }, 579 }, 580 }, 581 }, 582 }, 583 }, 584 }, 585 }, 586 }, 587 { 588 name: "subnets specified with IPv6 enabled", 589 clusterTemplate: &AzureClusterTemplate{ 590 ObjectMeta: metav1.ObjectMeta{ 591 Name: "test-cluster-template", 592 }, 593 Spec: AzureClusterTemplateSpec{ 594 Template: AzureClusterTemplateResource{ 595 Spec: AzureClusterTemplateResourceSpec{ 596 NetworkSpec: NetworkTemplateSpec{ 597 Subnets: SubnetTemplatesSpec{ 598 { 599 SubnetClassSpec: SubnetClassSpec{ 600 Role: SubnetControlPlane, 601 CIDRBlocks: []string{"2001:beef::1/64"}, 602 }, 603 }, 604 { 605 SubnetClassSpec: SubnetClassSpec{ 606 Role: SubnetNode, 607 CIDRBlocks: []string{"2001:beea::1/64"}, 608 }, 609 }, 610 }, 611 }, 612 }, 613 }, 614 }, 615 }, 616 outputTemplate: &AzureClusterTemplate{ 617 ObjectMeta: metav1.ObjectMeta{ 618 Name: "test-cluster-template", 619 }, 620 Spec: AzureClusterTemplateSpec{ 621 Template: AzureClusterTemplateResource{ 622 Spec: AzureClusterTemplateResourceSpec{ 623 NetworkSpec: NetworkTemplateSpec{ 624 Subnets: SubnetTemplatesSpec{ 625 { 626 SubnetClassSpec: SubnetClassSpec{ 627 Role: SubnetControlPlane, 628 CIDRBlocks: []string{"2001:beef::1/64"}, 629 }, 630 }, 631 { 632 SubnetClassSpec: SubnetClassSpec{ 633 Role: SubnetNode, 634 CIDRBlocks: []string{"2001:beea::1/64"}, 635 }, 636 }, 637 }, 638 }, 639 }, 640 }, 641 }, 642 }, 643 }, 644 } 645 646 for _, c := range cases { 647 tc := c 648 t.Run(tc.name, func(t *testing.T) { 649 t.Parallel() 650 tc.clusterTemplate.setSubnetsTemplateDefaults() 651 if !reflect.DeepEqual(tc.clusterTemplate, tc.outputTemplate) { 652 expected, _ := json.MarshalIndent(tc.outputTemplate, "", "\t") 653 actual, _ := json.MarshalIndent(tc.clusterTemplate, "", "\t") 654 t.Errorf("Expected %s, got %s", string(expected), string(actual)) 655 } 656 }) 657 } 658 } 659 660 func TestAPIServerLBClassDefaults(t *testing.T) { 661 cases := []struct { 662 name string 663 clusterTemplate *AzureClusterTemplate 664 outputTemplate *AzureClusterTemplate 665 }{ 666 { 667 name: "no lb", 668 clusterTemplate: &AzureClusterTemplate{ 669 ObjectMeta: metav1.ObjectMeta{ 670 Name: "test-cluster-template", 671 }, 672 Spec: AzureClusterTemplateSpec{ 673 Template: AzureClusterTemplateResource{ 674 Spec: AzureClusterTemplateResourceSpec{ 675 NetworkSpec: NetworkTemplateSpec{}, 676 }, 677 }, 678 }, 679 }, 680 outputTemplate: &AzureClusterTemplate{ 681 ObjectMeta: metav1.ObjectMeta{ 682 Name: "test-cluster-template", 683 }, 684 Spec: AzureClusterTemplateSpec{ 685 Template: AzureClusterTemplateResource{ 686 Spec: AzureClusterTemplateResourceSpec{ 687 NetworkSpec: NetworkTemplateSpec{ 688 APIServerLB: LoadBalancerClassSpec{ 689 SKU: SKUStandard, 690 Type: Public, 691 IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes), 692 }, 693 }, 694 }, 695 }, 696 }, 697 }, 698 }, 699 { 700 name: "internal lb", 701 clusterTemplate: &AzureClusterTemplate{ 702 ObjectMeta: metav1.ObjectMeta{ 703 Name: "test-cluster-template", 704 }, 705 Spec: AzureClusterTemplateSpec{ 706 Template: AzureClusterTemplateResource{ 707 Spec: AzureClusterTemplateResourceSpec{ 708 NetworkSpec: NetworkTemplateSpec{ 709 APIServerLB: LoadBalancerClassSpec{ 710 Type: Internal, 711 }, 712 }, 713 }, 714 }, 715 }, 716 }, 717 outputTemplate: &AzureClusterTemplate{ 718 ObjectMeta: metav1.ObjectMeta{ 719 Name: "test-cluster-template", 720 }, 721 Spec: AzureClusterTemplateSpec{ 722 Template: AzureClusterTemplateResource{ 723 Spec: AzureClusterTemplateResourceSpec{ 724 NetworkSpec: NetworkTemplateSpec{ 725 APIServerLB: LoadBalancerClassSpec{ 726 SKU: SKUStandard, 727 Type: Internal, 728 IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes), 729 }, 730 }, 731 }, 732 }, 733 }, 734 }, 735 }, 736 } 737 738 for _, c := range cases { 739 tc := c 740 t.Run(tc.name, func(t *testing.T) { 741 t.Parallel() 742 tc.clusterTemplate.Spec.Template.Spec.NetworkSpec.APIServerLB.setAPIServerLBDefaults() 743 if !reflect.DeepEqual(tc.clusterTemplate, tc.outputTemplate) { 744 expected, _ := json.MarshalIndent(tc.outputTemplate, "", "\t") 745 actual, _ := json.MarshalIndent(tc.clusterTemplate, "", "\t") 746 t.Errorf("Expected %s, got %s", string(expected), string(actual)) 747 } 748 }) 749 } 750 } 751 752 func TestNodeOutboundLBClassDefaults(t *testing.T) { 753 cases := []struct { 754 name string 755 clusterTemplate *AzureClusterTemplate 756 outputTemplate *AzureClusterTemplate 757 }{ 758 { 759 name: "default no lb for public clusters", 760 clusterTemplate: &AzureClusterTemplate{ 761 ObjectMeta: metav1.ObjectMeta{ 762 Name: "test-cluster-template", 763 }, 764 Spec: AzureClusterTemplateSpec{ 765 Template: AzureClusterTemplateResource{ 766 Spec: AzureClusterTemplateResourceSpec{ 767 NetworkSpec: NetworkTemplateSpec{ 768 APIServerLB: LoadBalancerClassSpec{Type: Public}, 769 Subnets: SubnetTemplatesSpec{ 770 { 771 SubnetClassSpec: SubnetClassSpec{ 772 Role: SubnetControlPlane, 773 }, 774 }, 775 { 776 SubnetClassSpec: SubnetClassSpec{ 777 Role: SubnetNode, 778 }, 779 }, 780 }, 781 }, 782 }, 783 }, 784 }, 785 }, 786 outputTemplate: &AzureClusterTemplate{ 787 ObjectMeta: metav1.ObjectMeta{ 788 Name: "test-cluster-template", 789 }, 790 Spec: AzureClusterTemplateSpec{ 791 Template: AzureClusterTemplateResource{ 792 Spec: AzureClusterTemplateResourceSpec{ 793 NetworkSpec: NetworkTemplateSpec{ 794 APIServerLB: LoadBalancerClassSpec{Type: Public}, 795 Subnets: SubnetTemplatesSpec{ 796 { 797 SubnetClassSpec: SubnetClassSpec{ 798 Role: SubnetControlPlane, 799 }, 800 }, 801 { 802 SubnetClassSpec: SubnetClassSpec{ 803 Role: SubnetNode, 804 }, 805 }, 806 }, 807 }, 808 }, 809 }, 810 }, 811 }, 812 }, 813 { 814 name: "default lb when IPv6 enabled", 815 clusterTemplate: &AzureClusterTemplate{ 816 ObjectMeta: metav1.ObjectMeta{ 817 Name: "test-cluster-template", 818 }, 819 Spec: AzureClusterTemplateSpec{ 820 Template: AzureClusterTemplateResource{ 821 Spec: AzureClusterTemplateResourceSpec{ 822 NetworkSpec: NetworkTemplateSpec{ 823 APIServerLB: LoadBalancerClassSpec{Type: Public}, 824 Subnets: SubnetTemplatesSpec{ 825 { 826 SubnetClassSpec: SubnetClassSpec{ 827 Role: SubnetControlPlane, 828 }, 829 }, 830 { 831 SubnetClassSpec: SubnetClassSpec{ 832 Role: SubnetNode, 833 CIDRBlocks: []string{"2001:beea::1/64"}, 834 }, 835 }, 836 }, 837 }, 838 }, 839 }, 840 }, 841 }, 842 outputTemplate: &AzureClusterTemplate{ 843 ObjectMeta: metav1.ObjectMeta{ 844 Name: "test-cluster-template", 845 }, 846 Spec: AzureClusterTemplateSpec{ 847 Template: AzureClusterTemplateResource{ 848 Spec: AzureClusterTemplateResourceSpec{ 849 NetworkSpec: NetworkTemplateSpec{ 850 APIServerLB: LoadBalancerClassSpec{Type: Public}, 851 Subnets: SubnetTemplatesSpec{ 852 { 853 SubnetClassSpec: SubnetClassSpec{ 854 Role: SubnetControlPlane, 855 }, 856 }, 857 { 858 SubnetClassSpec: SubnetClassSpec{ 859 Role: SubnetNode, 860 CIDRBlocks: []string{"2001:beea::1/64"}, 861 }, 862 }, 863 }, 864 NodeOutboundLB: &LoadBalancerClassSpec{ 865 SKU: SKUStandard, 866 Type: Public, 867 IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes), 868 }, 869 }, 870 }, 871 }, 872 }, 873 }, 874 }, 875 { 876 name: "IPv6 enabled on 1 of 2 node subnets", 877 clusterTemplate: &AzureClusterTemplate{ 878 ObjectMeta: metav1.ObjectMeta{ 879 Name: "test-cluster-template", 880 }, 881 Spec: AzureClusterTemplateSpec{ 882 Template: AzureClusterTemplateResource{ 883 Spec: AzureClusterTemplateResourceSpec{ 884 NetworkSpec: NetworkTemplateSpec{ 885 APIServerLB: LoadBalancerClassSpec{Type: Public}, 886 Subnets: SubnetTemplatesSpec{ 887 { 888 SubnetClassSpec: SubnetClassSpec{ 889 Role: SubnetControlPlane, 890 }, 891 }, 892 { 893 SubnetClassSpec: SubnetClassSpec{ 894 Role: SubnetNode, 895 CIDRBlocks: []string{"2001:beea::1/64"}, 896 }, 897 }, 898 { 899 SubnetClassSpec: SubnetClassSpec{ 900 Role: SubnetNode, 901 }, 902 }, 903 }, 904 }, 905 }, 906 }, 907 }, 908 }, 909 outputTemplate: &AzureClusterTemplate{ 910 ObjectMeta: metav1.ObjectMeta{ 911 Name: "test-cluster-template", 912 }, 913 Spec: AzureClusterTemplateSpec{ 914 Template: AzureClusterTemplateResource{ 915 Spec: AzureClusterTemplateResourceSpec{ 916 NetworkSpec: NetworkTemplateSpec{ 917 APIServerLB: LoadBalancerClassSpec{Type: Public}, 918 Subnets: SubnetTemplatesSpec{ 919 { 920 SubnetClassSpec: SubnetClassSpec{ 921 Role: SubnetControlPlane, 922 }, 923 }, 924 { 925 SubnetClassSpec: SubnetClassSpec{ 926 Role: SubnetNode, 927 CIDRBlocks: []string{"2001:beea::1/64"}, 928 }, 929 }, 930 { 931 SubnetClassSpec: SubnetClassSpec{ 932 Role: SubnetNode, 933 }, 934 }, 935 }, 936 NodeOutboundLB: &LoadBalancerClassSpec{ 937 SKU: SKUStandard, 938 Type: Public, 939 IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes), 940 }, 941 }, 942 }, 943 }, 944 }, 945 }, 946 }, 947 { 948 name: "multiple subnets specified with IPv6 enabled", 949 clusterTemplate: &AzureClusterTemplate{ 950 ObjectMeta: metav1.ObjectMeta{ 951 Name: "test-cluster-template", 952 }, 953 Spec: AzureClusterTemplateSpec{ 954 Template: AzureClusterTemplateResource{ 955 Spec: AzureClusterTemplateResourceSpec{ 956 NetworkSpec: NetworkTemplateSpec{ 957 APIServerLB: LoadBalancerClassSpec{Type: Public}, 958 Subnets: SubnetTemplatesSpec{ 959 { 960 SubnetClassSpec: SubnetClassSpec{ 961 Role: SubnetControlPlane, 962 }, 963 }, 964 { 965 SubnetClassSpec: SubnetClassSpec{ 966 Role: SubnetNode, 967 CIDRBlocks: []string{"2001:beea::1/64"}, 968 }, 969 }, 970 { 971 SubnetClassSpec: SubnetClassSpec{ 972 Role: SubnetNode, 973 CIDRBlocks: []string{"2002:beeb::1/64"}, 974 }, 975 }, 976 }, 977 }, 978 }, 979 }, 980 }, 981 }, 982 outputTemplate: &AzureClusterTemplate{ 983 ObjectMeta: metav1.ObjectMeta{ 984 Name: "test-cluster-template", 985 }, 986 Spec: AzureClusterTemplateSpec{ 987 Template: AzureClusterTemplateResource{ 988 Spec: AzureClusterTemplateResourceSpec{ 989 NetworkSpec: NetworkTemplateSpec{ 990 APIServerLB: LoadBalancerClassSpec{Type: Public}, 991 Subnets: SubnetTemplatesSpec{ 992 { 993 SubnetClassSpec: SubnetClassSpec{ 994 Role: SubnetControlPlane, 995 }, 996 }, 997 { 998 SubnetClassSpec: SubnetClassSpec{ 999 Role: SubnetNode, 1000 CIDRBlocks: []string{"2001:beea::1/64"}, 1001 }, 1002 }, 1003 { 1004 SubnetClassSpec: SubnetClassSpec{ 1005 Role: SubnetNode, 1006 CIDRBlocks: []string{"2002:beeb::1/64"}, 1007 }, 1008 }, 1009 }, 1010 NodeOutboundLB: &LoadBalancerClassSpec{ 1011 SKU: SKUStandard, 1012 Type: Public, 1013 IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes), 1014 }, 1015 }, 1016 }, 1017 }, 1018 }, 1019 }, 1020 }, 1021 { 1022 name: "multiple node subnets, Ipv6 not enabled in any of them", 1023 clusterTemplate: &AzureClusterTemplate{ 1024 ObjectMeta: metav1.ObjectMeta{ 1025 Name: "test-cluster-template", 1026 }, 1027 Spec: AzureClusterTemplateSpec{ 1028 Template: AzureClusterTemplateResource{ 1029 Spec: AzureClusterTemplateResourceSpec{ 1030 NetworkSpec: NetworkTemplateSpec{ 1031 APIServerLB: LoadBalancerClassSpec{Type: Public}, 1032 Subnets: SubnetTemplatesSpec{ 1033 { 1034 SubnetClassSpec: SubnetClassSpec{ 1035 Role: SubnetControlPlane, 1036 }, 1037 }, 1038 { 1039 SubnetClassSpec: SubnetClassSpec{ 1040 Role: SubnetNode, 1041 }, 1042 }, 1043 { 1044 SubnetClassSpec: SubnetClassSpec{ 1045 Role: SubnetNode, 1046 }, 1047 }, 1048 }, 1049 }, 1050 }, 1051 }, 1052 }, 1053 }, 1054 outputTemplate: &AzureClusterTemplate{ 1055 ObjectMeta: metav1.ObjectMeta{ 1056 Name: "test-cluster-template", 1057 }, 1058 Spec: AzureClusterTemplateSpec{ 1059 Template: AzureClusterTemplateResource{ 1060 Spec: AzureClusterTemplateResourceSpec{ 1061 NetworkSpec: NetworkTemplateSpec{ 1062 APIServerLB: LoadBalancerClassSpec{Type: Public}, 1063 Subnets: SubnetTemplatesSpec{ 1064 { 1065 SubnetClassSpec: SubnetClassSpec{ 1066 Role: SubnetControlPlane, 1067 }, 1068 }, 1069 { 1070 SubnetClassSpec: SubnetClassSpec{ 1071 Role: SubnetNode, 1072 }, 1073 }, 1074 { 1075 SubnetClassSpec: SubnetClassSpec{ 1076 Role: SubnetNode, 1077 }, 1078 }, 1079 }, 1080 }, 1081 }, 1082 }, 1083 }, 1084 }, 1085 }, 1086 { 1087 name: "no LB for private clusters", 1088 clusterTemplate: &AzureClusterTemplate{ 1089 ObjectMeta: metav1.ObjectMeta{ 1090 Name: "test-cluster-template", 1091 }, 1092 Spec: AzureClusterTemplateSpec{ 1093 Template: AzureClusterTemplateResource{ 1094 Spec: AzureClusterTemplateResourceSpec{ 1095 NetworkSpec: NetworkTemplateSpec{ 1096 APIServerLB: LoadBalancerClassSpec{Type: Internal}, 1097 }, 1098 }, 1099 }, 1100 }, 1101 }, 1102 outputTemplate: &AzureClusterTemplate{ 1103 ObjectMeta: metav1.ObjectMeta{ 1104 Name: "test-cluster-template", 1105 }, 1106 Spec: AzureClusterTemplateSpec{ 1107 Template: AzureClusterTemplateResource{ 1108 Spec: AzureClusterTemplateResourceSpec{ 1109 NetworkSpec: NetworkTemplateSpec{ 1110 APIServerLB: LoadBalancerClassSpec{Type: Internal}, 1111 }, 1112 }, 1113 }, 1114 }, 1115 }, 1116 }, 1117 { 1118 name: "NodeOutboundLB declared as input with non-default IdleTimeoutInMinutes", 1119 clusterTemplate: &AzureClusterTemplate{ 1120 ObjectMeta: metav1.ObjectMeta{ 1121 Name: "test-cluster-template", 1122 }, 1123 Spec: AzureClusterTemplateSpec{ 1124 Template: AzureClusterTemplateResource{ 1125 Spec: AzureClusterTemplateResourceSpec{ 1126 NetworkSpec: NetworkTemplateSpec{ 1127 APIServerLB: LoadBalancerClassSpec{Type: Internal}, 1128 NodeOutboundLB: &LoadBalancerClassSpec{ 1129 IdleTimeoutInMinutes: ptr.To[int32](15), 1130 }, 1131 }, 1132 }, 1133 }, 1134 }, 1135 }, 1136 outputTemplate: &AzureClusterTemplate{ 1137 ObjectMeta: metav1.ObjectMeta{ 1138 Name: "test-cluster-template", 1139 }, 1140 Spec: AzureClusterTemplateSpec{ 1141 Template: AzureClusterTemplateResource{ 1142 Spec: AzureClusterTemplateResourceSpec{ 1143 NetworkSpec: NetworkTemplateSpec{ 1144 APIServerLB: LoadBalancerClassSpec{Type: Internal}, 1145 NodeOutboundLB: &LoadBalancerClassSpec{ 1146 IdleTimeoutInMinutes: ptr.To[int32](15), 1147 SKU: SKUStandard, 1148 Type: Public, 1149 }, 1150 }, 1151 }, 1152 }, 1153 }, 1154 }, 1155 }, 1156 } 1157 1158 for _, c := range cases { 1159 tc := c 1160 t.Run(tc.name, func(t *testing.T) { 1161 t.Parallel() 1162 tc.clusterTemplate.setNodeOutboundLBDefaults() 1163 if !reflect.DeepEqual(tc.clusterTemplate, tc.outputTemplate) { 1164 expected, _ := json.MarshalIndent(tc.outputTemplate, "", "\t") 1165 actual, _ := json.MarshalIndent(tc.clusterTemplate, "", "\t") 1166 t.Errorf("Expected %s, got %s", string(expected), string(actual)) 1167 } 1168 }) 1169 } 1170 } 1171 1172 func TestControlPlaneOutboundLBTemplateDefaults(t *testing.T) { 1173 cases := []struct { 1174 name string 1175 clusterTemplate *AzureClusterTemplate 1176 outputTemplate *AzureClusterTemplate 1177 }{ 1178 { 1179 name: "no cp lb for public clusters", 1180 clusterTemplate: &AzureClusterTemplate{ 1181 ObjectMeta: metav1.ObjectMeta{ 1182 Name: "test-cluster-template", 1183 }, 1184 Spec: AzureClusterTemplateSpec{ 1185 Template: AzureClusterTemplateResource{ 1186 Spec: AzureClusterTemplateResourceSpec{ 1187 NetworkSpec: NetworkTemplateSpec{ 1188 APIServerLB: LoadBalancerClassSpec{Type: Public}, 1189 }, 1190 }, 1191 }, 1192 }, 1193 }, 1194 outputTemplate: &AzureClusterTemplate{ 1195 ObjectMeta: metav1.ObjectMeta{ 1196 Name: "test-cluster-template", 1197 }, 1198 Spec: AzureClusterTemplateSpec{ 1199 Template: AzureClusterTemplateResource{ 1200 Spec: AzureClusterTemplateResourceSpec{ 1201 NetworkSpec: NetworkTemplateSpec{ 1202 APIServerLB: LoadBalancerClassSpec{Type: Public}, 1203 }, 1204 }, 1205 }, 1206 }, 1207 }, 1208 }, 1209 { 1210 name: "no cp lb for private clusters", 1211 clusterTemplate: &AzureClusterTemplate{ 1212 ObjectMeta: metav1.ObjectMeta{ 1213 Name: "test-cluster-template", 1214 }, 1215 Spec: AzureClusterTemplateSpec{ 1216 Template: AzureClusterTemplateResource{ 1217 Spec: AzureClusterTemplateResourceSpec{ 1218 NetworkSpec: NetworkTemplateSpec{ 1219 APIServerLB: LoadBalancerClassSpec{Type: Internal}, 1220 }, 1221 }, 1222 }, 1223 }, 1224 }, 1225 outputTemplate: &AzureClusterTemplate{ 1226 ObjectMeta: metav1.ObjectMeta{ 1227 Name: "test-cluster-template", 1228 }, 1229 Spec: AzureClusterTemplateSpec{ 1230 Template: AzureClusterTemplateResource{ 1231 Spec: AzureClusterTemplateResourceSpec{ 1232 NetworkSpec: NetworkTemplateSpec{ 1233 APIServerLB: LoadBalancerClassSpec{Type: Internal}, 1234 }, 1235 }, 1236 }, 1237 }, 1238 }, 1239 }, 1240 } 1241 1242 for _, c := range cases { 1243 tc := c 1244 t.Run(tc.name, func(t *testing.T) { 1245 t.Parallel() 1246 setControlPlaneOutboundLBDefaults( 1247 tc.clusterTemplate.Spec.Template.Spec.NetworkSpec.ControlPlaneOutboundLB, 1248 tc.clusterTemplate.Spec.Template.Spec.NetworkSpec.APIServerLB.Type, 1249 ) 1250 if !reflect.DeepEqual(tc.clusterTemplate, tc.outputTemplate) { 1251 expected, _ := json.MarshalIndent(tc.outputTemplate, "", "\t") 1252 actual, _ := json.MarshalIndent(tc.clusterTemplate, "", "\t") 1253 t.Errorf("Expected %s, got %s", string(expected), string(actual)) 1254 } 1255 }) 1256 } 1257 } 1258 1259 func TestBastionTemplateDefaults(t *testing.T) { 1260 cases := []struct { 1261 name string 1262 clusterTemplate *AzureClusterTemplate 1263 outputTemplate *AzureClusterTemplate 1264 }{ 1265 { 1266 name: "no bastion set", 1267 clusterTemplate: &AzureClusterTemplate{ 1268 ObjectMeta: metav1.ObjectMeta{ 1269 Name: "test-cluster-template", 1270 }, 1271 Spec: AzureClusterTemplateSpec{ 1272 Template: AzureClusterTemplateResource{ 1273 Spec: AzureClusterTemplateResourceSpec{}, 1274 }, 1275 }, 1276 }, 1277 outputTemplate: &AzureClusterTemplate{ 1278 ObjectMeta: metav1.ObjectMeta{ 1279 Name: "test-cluster-template", 1280 }, 1281 Spec: AzureClusterTemplateSpec{ 1282 Template: AzureClusterTemplateResource{ 1283 Spec: AzureClusterTemplateResourceSpec{}, 1284 }, 1285 }, 1286 }, 1287 }, 1288 { 1289 name: "azure bastion enabled with no settings", 1290 clusterTemplate: &AzureClusterTemplate{ 1291 ObjectMeta: metav1.ObjectMeta{ 1292 Name: "test-cluster-template", 1293 }, 1294 Spec: AzureClusterTemplateSpec{ 1295 Template: AzureClusterTemplateResource{ 1296 Spec: AzureClusterTemplateResourceSpec{ 1297 BastionSpec: BastionTemplateSpec{ 1298 AzureBastion: &AzureBastionTemplateSpec{}, 1299 }, 1300 }, 1301 }, 1302 }, 1303 }, 1304 outputTemplate: &AzureClusterTemplate{ 1305 ObjectMeta: metav1.ObjectMeta{ 1306 Name: "test-cluster-template", 1307 }, 1308 Spec: AzureClusterTemplateSpec{ 1309 Template: AzureClusterTemplateResource{ 1310 Spec: AzureClusterTemplateResourceSpec{ 1311 BastionSpec: BastionTemplateSpec{ 1312 AzureBastion: &AzureBastionTemplateSpec{ 1313 Subnet: SubnetTemplateSpec{ 1314 SubnetClassSpec: SubnetClassSpec{ 1315 Role: DefaultAzureBastionSubnetRole, 1316 CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR}, 1317 }, 1318 }, 1319 }, 1320 }, 1321 }, 1322 }, 1323 }, 1324 }, 1325 }, 1326 { 1327 name: "azure bastion enabled with subnet partially set", 1328 clusterTemplate: &AzureClusterTemplate{ 1329 ObjectMeta: metav1.ObjectMeta{ 1330 Name: "test-cluster-template", 1331 }, 1332 Spec: AzureClusterTemplateSpec{ 1333 Template: AzureClusterTemplateResource{ 1334 Spec: AzureClusterTemplateResourceSpec{ 1335 BastionSpec: BastionTemplateSpec{ 1336 AzureBastion: &AzureBastionTemplateSpec{ 1337 Subnet: SubnetTemplateSpec{ 1338 SubnetClassSpec: SubnetClassSpec{}, 1339 }, 1340 }, 1341 }, 1342 }, 1343 }, 1344 }, 1345 }, 1346 outputTemplate: &AzureClusterTemplate{ 1347 ObjectMeta: metav1.ObjectMeta{ 1348 Name: "test-cluster-template", 1349 }, 1350 Spec: AzureClusterTemplateSpec{ 1351 Template: AzureClusterTemplateResource{ 1352 Spec: AzureClusterTemplateResourceSpec{ 1353 BastionSpec: BastionTemplateSpec{ 1354 AzureBastion: &AzureBastionTemplateSpec{ 1355 Subnet: SubnetTemplateSpec{ 1356 SubnetClassSpec: SubnetClassSpec{ 1357 Role: DefaultAzureBastionSubnetRole, 1358 CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR}, 1359 }, 1360 }, 1361 }, 1362 }, 1363 }, 1364 }, 1365 }, 1366 }, 1367 }, 1368 { 1369 name: "azure bastion enabled with subnet fully set", 1370 clusterTemplate: &AzureClusterTemplate{ 1371 ObjectMeta: metav1.ObjectMeta{ 1372 Name: "test-cluster-template", 1373 }, 1374 Spec: AzureClusterTemplateSpec{ 1375 Template: AzureClusterTemplateResource{ 1376 Spec: AzureClusterTemplateResourceSpec{ 1377 BastionSpec: BastionTemplateSpec{ 1378 AzureBastion: &AzureBastionTemplateSpec{ 1379 Subnet: SubnetTemplateSpec{ 1380 SubnetClassSpec: SubnetClassSpec{ 1381 Role: DefaultAzureBastionSubnetRole, 1382 CIDRBlocks: []string{"10.10.0.0/16"}, 1383 }, 1384 }, 1385 }, 1386 }, 1387 }, 1388 }, 1389 }, 1390 }, 1391 outputTemplate: &AzureClusterTemplate{ 1392 ObjectMeta: metav1.ObjectMeta{ 1393 Name: "test-cluster-template", 1394 }, 1395 Spec: AzureClusterTemplateSpec{ 1396 Template: AzureClusterTemplateResource{ 1397 Spec: AzureClusterTemplateResourceSpec{ 1398 BastionSpec: BastionTemplateSpec{ 1399 AzureBastion: &AzureBastionTemplateSpec{ 1400 Subnet: SubnetTemplateSpec{ 1401 SubnetClassSpec: SubnetClassSpec{ 1402 Role: DefaultAzureBastionSubnetRole, 1403 CIDRBlocks: []string{"10.10.0.0/16"}, 1404 }, 1405 }, 1406 }, 1407 }, 1408 }, 1409 }, 1410 }, 1411 }, 1412 }, 1413 } 1414 1415 for _, c := range cases { 1416 tc := c 1417 t.Run(tc.name, func(t *testing.T) { 1418 t.Parallel() 1419 tc.clusterTemplate.setBastionTemplateDefaults() 1420 if !reflect.DeepEqual(tc.clusterTemplate, tc.outputTemplate) { 1421 expected, _ := json.MarshalIndent(tc.outputTemplate, "", "\t") 1422 actual, _ := json.MarshalIndent(tc.clusterTemplate, "", "\t") 1423 t.Errorf("Expected %s, got %s", string(expected), string(actual)) 1424 } 1425 }) 1426 } 1427 }