sigs.k8s.io/cluster-api@v1.7.1/internal/controllers/topology/cluster/patches/variables/variables_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 variables 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "testing" 23 24 . "github.com/onsi/gomega" 25 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 28 "k8s.io/utils/ptr" 29 30 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 31 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 32 runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" 33 "sigs.k8s.io/cluster-api/internal/test/builder" 34 ) 35 36 func TestGlobal(t *testing.T) { 37 tests := []struct { 38 name string 39 clusterTopology *clusterv1.Topology 40 cluster *clusterv1.Cluster 41 forPatch string 42 variableDefinitionsForPatch map[string]bool 43 want []runtimehooksv1.Variable 44 }{ 45 { 46 name: "Should calculate global variables", 47 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 48 forPatch: "patch1", 49 clusterTopology: &clusterv1.Topology{ 50 Variables: []clusterv1.ClusterVariable{ 51 { 52 Name: "location", 53 Value: toJSON("\"us-central\""), 54 }, 55 { 56 Name: "cpu", 57 Value: toJSON("8"), 58 }, 59 { 60 // This is blocked by a webhook, but let's make sure that the user-defined 61 // variable is overwritten by the builtin variable anyway. 62 Name: "builtin", 63 Value: toJSON("8"), 64 }, 65 }, 66 }, 67 cluster: &clusterv1.Cluster{ 68 ObjectMeta: metav1.ObjectMeta{ 69 Name: "cluster1", 70 Namespace: metav1.NamespaceDefault, 71 }, 72 Spec: clusterv1.ClusterSpec{ 73 Topology: &clusterv1.Topology{ 74 Class: "clusterClass1", 75 Version: "v1.21.1", 76 }, 77 ClusterNetwork: &clusterv1.ClusterNetwork{ 78 Services: &clusterv1.NetworkRanges{ 79 CIDRBlocks: []string{"10.10.10.1/24"}, 80 }, 81 Pods: &clusterv1.NetworkRanges{ 82 CIDRBlocks: []string{"11.10.10.1/24"}, 83 }, 84 ServiceDomain: "cluster.local", 85 }, 86 }, 87 }, 88 want: []runtimehooksv1.Variable{ 89 { 90 Name: "location", 91 Value: toJSON("\"us-central\""), 92 }, 93 { 94 Name: "cpu", 95 Value: toJSON("8"), 96 }, 97 { 98 Name: runtimehooksv1.BuiltinsName, 99 Value: toJSONCompact(`{ 100 "cluster":{ 101 "name": "cluster1", 102 "namespace": "default", 103 "topology":{ 104 "version": "v1.21.1", 105 "class": "clusterClass1" 106 }, 107 "network":{ 108 "serviceDomain":"cluster.local", 109 "services":["10.10.10.1/24"], 110 "pods":["11.10.10.1/24"], 111 "ipFamily": "IPv4" 112 } 113 }}`), 114 }, 115 }, 116 }, 117 { 118 name: "Should calculate global variables for a given forPatch", 119 forPatch: "patch1", 120 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 121 clusterTopology: &clusterv1.Topology{ 122 Variables: []clusterv1.ClusterVariable{ 123 { 124 Name: "location", 125 Value: toJSON("\"us-central\""), 126 DefinitionFrom: "patch1", 127 }, 128 { 129 Name: "location", 130 Value: toJSON("\"internal.proxy.com\""), 131 // This variable should be excluded because it is defined for a different patch. 132 DefinitionFrom: "anotherPatch", 133 }, 134 { 135 Name: "https-proxy", 136 Value: toJSON("\"internal.proxy.com\""), 137 // This variable should be excluded because it is not among variableDefinitionsForPatch. 138 DefinitionFrom: "", 139 }, 140 141 { 142 Name: "cpu", 143 Value: toJSON("8"), 144 // This variable should be included because it is defined for all patches. 145 }, 146 }, 147 }, 148 cluster: &clusterv1.Cluster{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Name: "cluster1", 151 Namespace: metav1.NamespaceDefault, 152 }, 153 Spec: clusterv1.ClusterSpec{ 154 Topology: &clusterv1.Topology{ 155 Class: "clusterClass1", 156 Version: "v1.21.1", 157 }, 158 ClusterNetwork: &clusterv1.ClusterNetwork{ 159 Services: &clusterv1.NetworkRanges{ 160 CIDRBlocks: []string{"10.10.10.1/24"}, 161 }, 162 Pods: &clusterv1.NetworkRanges{ 163 CIDRBlocks: []string{"11.10.10.1/24"}, 164 }, 165 ServiceDomain: "cluster.local", 166 }, 167 }, 168 }, 169 want: []runtimehooksv1.Variable{ 170 { 171 Name: "location", 172 Value: toJSON("\"us-central\""), 173 }, 174 { 175 Name: "cpu", 176 Value: toJSON("8"), 177 }, 178 { 179 Name: runtimehooksv1.BuiltinsName, 180 Value: toJSONCompact(`{ 181 "cluster":{ 182 "name": "cluster1", 183 "namespace": "default", 184 "topology":{ 185 "version": "v1.21.1", 186 "class": "clusterClass1" 187 }, 188 "network":{ 189 "serviceDomain":"cluster.local", 190 "services":["10.10.10.1/24"], 191 "pods":["11.10.10.1/24"], 192 "ipFamily": "IPv4" 193 } 194 }}`), 195 }, 196 }, 197 }, 198 { 199 name: "Should calculate when serviceDomain is not set", 200 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 201 forPatch: "patch1", 202 clusterTopology: &clusterv1.Topology{ 203 Variables: []clusterv1.ClusterVariable{ 204 { 205 Name: "location", 206 Value: toJSON("\"us-central\""), 207 }, 208 { 209 Name: "cpu", 210 Value: toJSON("8"), 211 }, 212 { 213 // This is blocked by a webhook, but let's make sure that the user-defined 214 // variable is overwritten by the builtin variable anyway. 215 Name: "builtin", 216 Value: toJSON("8"), 217 }, 218 }, 219 }, 220 cluster: &clusterv1.Cluster{ 221 ObjectMeta: metav1.ObjectMeta{ 222 Name: "cluster1", 223 Namespace: metav1.NamespaceDefault, 224 }, 225 Spec: clusterv1.ClusterSpec{ 226 Topology: &clusterv1.Topology{ 227 Class: "clusterClass1", 228 Version: "v1.21.1", 229 }, 230 ClusterNetwork: &clusterv1.ClusterNetwork{ 231 Services: &clusterv1.NetworkRanges{ 232 CIDRBlocks: []string{"10.10.10.1/24"}, 233 }, 234 Pods: &clusterv1.NetworkRanges{ 235 CIDRBlocks: []string{"11.10.10.1/24"}, 236 }, 237 }, 238 }, 239 }, 240 want: []runtimehooksv1.Variable{ 241 { 242 Name: "location", 243 Value: toJSON("\"us-central\""), 244 }, 245 { 246 Name: "cpu", 247 Value: toJSON("8"), 248 }, 249 { 250 Name: runtimehooksv1.BuiltinsName, 251 Value: toJSONCompact(`{ 252 "cluster":{ 253 "name": "cluster1", 254 "namespace": "default", 255 "topology":{ 256 "version": "v1.21.1", 257 "class": "clusterClass1" 258 }, 259 "network":{ 260 "services":["10.10.10.1/24"], 261 "pods":["11.10.10.1/24"], 262 "ipFamily": "IPv4" 263 } 264 }}`), 265 }, 266 }, 267 }, 268 { 269 name: "Should calculate where some variables are nil", 270 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 271 forPatch: "patch1", 272 clusterTopology: &clusterv1.Topology{ 273 Variables: []clusterv1.ClusterVariable{ 274 { 275 Name: "location", 276 Value: toJSON("\"us-central\""), 277 }, 278 { 279 Name: "cpu", 280 Value: toJSON("8"), 281 }, 282 { 283 // This is blocked by a webhook, but let's make sure that the user-defined 284 // variable is overwritten by the builtin variable anyway. 285 Name: "builtin", 286 Value: toJSON("8"), 287 }, 288 }, 289 }, 290 cluster: &clusterv1.Cluster{ 291 ObjectMeta: metav1.ObjectMeta{ 292 Name: "cluster1", 293 Namespace: metav1.NamespaceDefault, 294 }, 295 Spec: clusterv1.ClusterSpec{ 296 Topology: &clusterv1.Topology{ 297 Class: "clusterClass1", 298 Version: "v1.21.1", 299 }, 300 ClusterNetwork: &clusterv1.ClusterNetwork{ 301 Services: nil, 302 Pods: &clusterv1.NetworkRanges{}, 303 ServiceDomain: "cluster.local", 304 }, 305 }, 306 }, 307 want: []runtimehooksv1.Variable{ 308 { 309 Name: "location", 310 Value: toJSON("\"us-central\""), 311 }, 312 { 313 Name: "cpu", 314 Value: toJSON("8"), 315 }, 316 { 317 Name: runtimehooksv1.BuiltinsName, 318 Value: toJSONCompact(`{ 319 "cluster":{ 320 "name": "cluster1", 321 "namespace": "default", 322 "topology":{ 323 "version": "v1.21.1", 324 "class": "clusterClass1" 325 }, 326 "network":{ 327 "serviceDomain":"cluster.local", 328 "ipFamily": "IPv4" 329 } 330 }}`), 331 }, 332 }, 333 }, 334 { 335 name: "Should calculate where ClusterNetwork is nil", 336 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 337 forPatch: "patch1", 338 clusterTopology: &clusterv1.Topology{ 339 Variables: []clusterv1.ClusterVariable{ 340 { 341 Name: "location", 342 Value: toJSON("\"us-central\""), 343 }, 344 { 345 Name: "cpu", 346 Value: toJSON("8"), 347 }, 348 { 349 // This is blocked by a webhook, but let's make sure that the user-defined 350 // variable is overwritten by the builtin variable anyway. 351 Name: "builtin", 352 Value: toJSON("8"), 353 }, 354 }, 355 }, 356 cluster: &clusterv1.Cluster{ 357 ObjectMeta: metav1.ObjectMeta{ 358 Name: "cluster1", 359 Namespace: metav1.NamespaceDefault, 360 }, 361 Spec: clusterv1.ClusterSpec{ 362 Topology: &clusterv1.Topology{ 363 Class: "clusterClass1", 364 Version: "v1.21.1", 365 }, 366 ClusterNetwork: nil, 367 }, 368 }, 369 want: []runtimehooksv1.Variable{ 370 { 371 Name: "location", 372 Value: toJSON("\"us-central\""), 373 }, 374 { 375 Name: "cpu", 376 Value: toJSON("8"), 377 }, 378 { 379 Name: runtimehooksv1.BuiltinsName, 380 Value: toJSONCompact(`{ 381 "cluster":{ 382 "name": "cluster1", 383 "namespace": "default", 384 "topology":{ 385 "version": "v1.21.1", 386 "class": "clusterClass1" 387 } 388 }}`), 389 }, 390 }, 391 }, 392 } 393 for _, tt := range tests { 394 t.Run(tt.name, func(t *testing.T) { 395 g := NewWithT(t) 396 397 got, err := Global(tt.clusterTopology, tt.cluster, tt.forPatch, tt.variableDefinitionsForPatch) 398 g.Expect(err).ToNot(HaveOccurred()) 399 g.Expect(got).To(BeComparableTo(tt.want)) 400 }) 401 } 402 } 403 404 func TestControlPlane(t *testing.T) { 405 tests := []struct { 406 name string 407 controlPlaneTopology *clusterv1.ControlPlaneTopology 408 controlPlane *unstructured.Unstructured 409 controlPlaneInfrastructureMachineTemplate *unstructured.Unstructured 410 want []runtimehooksv1.Variable 411 }{ 412 { 413 name: "Should calculate ControlPlane variables", 414 controlPlaneTopology: &clusterv1.ControlPlaneTopology{ 415 Replicas: ptr.To[int32](3), 416 }, 417 controlPlane: builder.ControlPlane(metav1.NamespaceDefault, "controlPlane1"). 418 WithReplicas(3). 419 WithVersion("v1.21.1"). 420 Build(), 421 want: []runtimehooksv1.Variable{ 422 { 423 Name: runtimehooksv1.BuiltinsName, 424 Value: toJSONCompact(`{ 425 "controlPlane":{ 426 "version": "v1.21.1", 427 "name":"controlPlane1", 428 "replicas":3 429 }}`), 430 }, 431 }, 432 }, 433 { 434 name: "Should calculate ControlPlane variables, replicas not set", 435 controlPlaneTopology: &clusterv1.ControlPlaneTopology{}, 436 controlPlane: builder.ControlPlane(metav1.NamespaceDefault, "controlPlane1"). 437 WithVersion("v1.21.1"). 438 Build(), 439 want: []runtimehooksv1.Variable{ 440 { 441 Name: runtimehooksv1.BuiltinsName, 442 Value: toJSONCompact(`{ 443 "controlPlane":{ 444 "version": "v1.21.1", 445 "name":"controlPlane1" 446 }}`), 447 }, 448 }, 449 }, 450 { 451 name: "Should calculate ControlPlane variables with InfrastructureMachineTemplate", 452 controlPlaneTopology: &clusterv1.ControlPlaneTopology{ 453 Replicas: ptr.To[int32](3), 454 }, 455 controlPlane: builder.ControlPlane(metav1.NamespaceDefault, "controlPlane1"). 456 WithReplicas(3). 457 WithVersion("v1.21.1"). 458 Build(), 459 controlPlaneInfrastructureMachineTemplate: builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "controlPlaneInfrastructureMachineTemplate1"). 460 Build(), 461 want: []runtimehooksv1.Variable{ 462 { 463 Name: runtimehooksv1.BuiltinsName, 464 Value: toJSONCompact(`{ 465 "controlPlane":{ 466 "version": "v1.21.1", 467 "name":"controlPlane1", 468 "replicas":3, 469 "machineTemplate":{ 470 "infrastructureRef":{ 471 "name": "controlPlaneInfrastructureMachineTemplate1" 472 } 473 } 474 }}`), 475 }, 476 }, 477 }, 478 } 479 for _, tt := range tests { 480 t.Run(tt.name, func(t *testing.T) { 481 g := NewWithT(t) 482 483 got, err := ControlPlane(tt.controlPlaneTopology, tt.controlPlane, tt.controlPlaneInfrastructureMachineTemplate) 484 g.Expect(err).ToNot(HaveOccurred()) 485 g.Expect(got).To(BeComparableTo(tt.want)) 486 }) 487 } 488 } 489 490 func TestMachineDeployment(t *testing.T) { 491 tests := []struct { 492 name string 493 mdTopology *clusterv1.MachineDeploymentTopology 494 forPatch string 495 variableDefinitionsForPatch map[string]bool 496 md *clusterv1.MachineDeployment 497 mdBootstrapTemplate *unstructured.Unstructured 498 mdInfrastructureMachineTemplate *unstructured.Unstructured 499 want []runtimehooksv1.Variable 500 }{ 501 { 502 name: "Should calculate MachineDeployment variables", 503 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 504 forPatch: "patch1", 505 mdTopology: &clusterv1.MachineDeploymentTopology{ 506 Replicas: ptr.To[int32](3), 507 Name: "md-topology", 508 Class: "md-class", 509 Variables: &clusterv1.MachineDeploymentVariables{ 510 Overrides: []clusterv1.ClusterVariable{ 511 { 512 Name: "location", 513 Value: toJSON("\"us-central\""), 514 }, 515 { 516 Name: "cpu", 517 Value: toJSON("8"), 518 }, 519 }, 520 }, 521 }, 522 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 523 WithReplicas(3). 524 WithVersion("v1.21.1"). 525 Build(), 526 want: []runtimehooksv1.Variable{ 527 { 528 Name: "location", 529 Value: toJSON("\"us-central\""), 530 }, 531 { 532 Name: "cpu", 533 Value: toJSON("8"), 534 }, 535 { 536 Name: runtimehooksv1.BuiltinsName, 537 Value: toJSONCompact(`{ 538 "machineDeployment":{ 539 "version": "v1.21.1", 540 "class": "md-class", 541 "name": "md1", 542 "topologyName": "md-topology", 543 "replicas":3 544 }}`), 545 }, 546 }, 547 }, 548 { 549 name: "Should calculate MachineDeployment variables for a given patch name", 550 forPatch: "patch1", 551 variableDefinitionsForPatch: map[string]bool{ 552 "location": true, 553 "cpu": true, 554 }, 555 mdTopology: &clusterv1.MachineDeploymentTopology{ 556 Replicas: ptr.To[int32](3), 557 Name: "md-topology", 558 Class: "md-class", 559 Variables: &clusterv1.MachineDeploymentVariables{ 560 Overrides: []clusterv1.ClusterVariable{ 561 { 562 Name: "location", 563 Value: toJSON("\"us-central\""), 564 DefinitionFrom: "patch1", 565 }, 566 { 567 Name: "location", 568 Value: toJSON("\"us-east\""), 569 // This variable should be excluded because it is defined for a different patch. 570 DefinitionFrom: "anotherPatch", 571 }, 572 573 { 574 Name: "http-proxy", 575 Value: toJSON("\"internal.proxy.com\""), 576 // This variable should be excluded because it is not in variableDefinitionsForPatch. 577 DefinitionFrom: "", 578 }, 579 { 580 Name: "cpu", 581 Value: toJSON("8"), 582 // This variable should be included because it is defined for all patches. 583 }, 584 }, 585 }, 586 }, 587 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 588 WithReplicas(3). 589 WithVersion("v1.21.1"). 590 Build(), 591 want: []runtimehooksv1.Variable{ 592 { 593 Name: "location", 594 Value: toJSON("\"us-central\""), 595 }, 596 { 597 Name: "cpu", 598 Value: toJSON("8"), 599 }, 600 { 601 Name: runtimehooksv1.BuiltinsName, 602 Value: toJSONCompact(`{ 603 "machineDeployment":{ 604 "version": "v1.21.1", 605 "class": "md-class", 606 "name": "md1", 607 "topologyName": "md-topology", 608 "replicas":3 609 }}`), 610 }, 611 }, 612 }, 613 { 614 name: "Should calculate MachineDeployment variables (without overrides)", 615 forPatch: "patch1", 616 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 617 mdTopology: &clusterv1.MachineDeploymentTopology{ 618 Replicas: ptr.To[int32](3), 619 Name: "md-topology", 620 Class: "md-class", 621 }, 622 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 623 WithReplicas(3). 624 WithVersion("v1.21.1"). 625 Build(), 626 want: []runtimehooksv1.Variable{ 627 { 628 Name: runtimehooksv1.BuiltinsName, 629 Value: toJSONCompact(`{ 630 "machineDeployment":{ 631 "version": "v1.21.1", 632 "class": "md-class", 633 "name": "md1", 634 "topologyName": "md-topology", 635 "replicas":3 636 }}`), 637 }, 638 }, 639 }, 640 { 641 name: "Should calculate MachineDeployment variables, replicas not set", 642 forPatch: "patch1", 643 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 644 mdTopology: &clusterv1.MachineDeploymentTopology{ 645 Name: "md-topology", 646 Class: "md-class", 647 Variables: &clusterv1.MachineDeploymentVariables{ 648 Overrides: []clusterv1.ClusterVariable{ 649 { 650 Name: "location", 651 Value: toJSON("\"us-central\""), 652 }, 653 { 654 Name: "cpu", 655 Value: toJSON("8"), 656 }, 657 }, 658 }, 659 }, 660 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 661 WithVersion("v1.21.1"). 662 Build(), 663 want: []runtimehooksv1.Variable{ 664 { 665 Name: "location", 666 Value: toJSON("\"us-central\""), 667 }, 668 { 669 Name: "cpu", 670 Value: toJSON("8"), 671 }, 672 { 673 Name: runtimehooksv1.BuiltinsName, 674 Value: toJSONCompact(`{ 675 "machineDeployment":{ 676 "version": "v1.21.1", 677 "class": "md-class", 678 "name": "md1", 679 "topologyName": "md-topology" 680 }}`), 681 }, 682 }, 683 }, 684 { 685 name: "Should calculate MachineDeployment variables with BoostrapTemplate", 686 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 687 forPatch: "patch1", 688 mdTopology: &clusterv1.MachineDeploymentTopology{ 689 Replicas: ptr.To[int32](3), 690 Name: "md-topology", 691 Class: "md-class", 692 Variables: &clusterv1.MachineDeploymentVariables{ 693 Overrides: []clusterv1.ClusterVariable{ 694 { 695 Name: "location", 696 Value: toJSON("\"us-central\""), 697 }, 698 { 699 Name: "cpu", 700 Value: toJSON("8"), 701 }, 702 }, 703 }, 704 }, 705 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 706 WithReplicas(3). 707 WithVersion("v1.21.1"). 708 Build(), 709 mdBootstrapTemplate: builder.BootstrapTemplate(metav1.NamespaceDefault, "mdBT1").Build(), 710 want: []runtimehooksv1.Variable{ 711 { 712 Name: "location", 713 Value: toJSON("\"us-central\""), 714 }, 715 { 716 Name: "cpu", 717 Value: toJSON("8"), 718 }, 719 { 720 Name: runtimehooksv1.BuiltinsName, 721 Value: toJSONCompact(`{ 722 "machineDeployment":{ 723 "version": "v1.21.1", 724 "class": "md-class", 725 "name": "md1", 726 "topologyName": "md-topology", 727 "replicas":3, 728 "bootstrap":{ 729 "configRef":{ 730 "name": "mdBT1" 731 } 732 } 733 }}`), 734 }, 735 }, 736 }, 737 { 738 name: "Should calculate MachineDeployment variables with InfrastructureMachineTemplate", 739 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 740 forPatch: "patch1", 741 mdTopology: &clusterv1.MachineDeploymentTopology{ 742 Replicas: ptr.To[int32](3), 743 Name: "md-topology", 744 Class: "md-class", 745 Variables: &clusterv1.MachineDeploymentVariables{ 746 Overrides: []clusterv1.ClusterVariable{ 747 { 748 Name: "location", 749 Value: toJSON("\"us-central\""), 750 }, 751 { 752 Name: "cpu", 753 Value: toJSON("8"), 754 }, 755 }, 756 }, 757 }, 758 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 759 WithReplicas(3). 760 WithVersion("v1.21.1"). 761 Build(), 762 mdInfrastructureMachineTemplate: builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "mdIMT1").Build(), 763 want: []runtimehooksv1.Variable{ 764 { 765 Name: "location", 766 Value: toJSON("\"us-central\""), 767 }, 768 { 769 Name: "cpu", 770 Value: toJSON("8"), 771 }, 772 { 773 Name: runtimehooksv1.BuiltinsName, 774 Value: toJSONCompact(`{ 775 "machineDeployment":{ 776 "version": "v1.21.1", 777 "class": "md-class", 778 "name": "md1", 779 "topologyName": "md-topology", 780 "replicas":3, 781 "infrastructureRef":{ 782 "name": "mdIMT1" 783 } 784 }}`), 785 }, 786 }, 787 }, 788 { 789 name: "Should calculate MachineDeployment variables with BootstrapTemplate and InfrastructureMachineTemplate", 790 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 791 forPatch: "patch1", 792 mdTopology: &clusterv1.MachineDeploymentTopology{ 793 Replicas: ptr.To[int32](3), 794 Name: "md-topology", 795 Class: "md-class", 796 Variables: &clusterv1.MachineDeploymentVariables{ 797 Overrides: []clusterv1.ClusterVariable{ 798 { 799 Name: "location", 800 Value: toJSON("\"us-central\""), 801 }, 802 { 803 Name: "cpu", 804 Value: toJSON("8"), 805 }, 806 }, 807 }, 808 }, 809 md: builder.MachineDeployment(metav1.NamespaceDefault, "md1"). 810 WithReplicas(3). 811 WithVersion("v1.21.1"). 812 Build(), 813 mdBootstrapTemplate: builder.BootstrapTemplate(metav1.NamespaceDefault, "mdBT1").Build(), 814 mdInfrastructureMachineTemplate: builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "mdIMT1").Build(), 815 want: []runtimehooksv1.Variable{ 816 { 817 Name: "location", 818 Value: toJSON("\"us-central\""), 819 }, 820 { 821 Name: "cpu", 822 Value: toJSON("8"), 823 }, 824 { 825 Name: runtimehooksv1.BuiltinsName, 826 Value: toJSONCompact(`{ 827 "machineDeployment":{ 828 "version": "v1.21.1", 829 "class": "md-class", 830 "name": "md1", 831 "topologyName": "md-topology", 832 "replicas":3, 833 "bootstrap":{ 834 "configRef":{ 835 "name": "mdBT1" 836 } 837 }, 838 "infrastructureRef":{ 839 "name": "mdIMT1" 840 } 841 }}`), 842 }, 843 }, 844 }, 845 } 846 for _, tt := range tests { 847 t.Run(tt.name, func(t *testing.T) { 848 g := NewWithT(t) 849 850 got, err := MachineDeployment(tt.mdTopology, tt.md, tt.mdBootstrapTemplate, tt.mdInfrastructureMachineTemplate, tt.forPatch, tt.variableDefinitionsForPatch) 851 g.Expect(err).ToNot(HaveOccurred()) 852 g.Expect(got).To(BeComparableTo(tt.want)) 853 }) 854 } 855 } 856 857 func TestMachinePool(t *testing.T) { 858 tests := []struct { 859 name string 860 mpTopology *clusterv1.MachinePoolTopology 861 forPatch string 862 variableDefinitionsForPatch map[string]bool 863 mp *expv1.MachinePool 864 mpBootstrapConfig *unstructured.Unstructured 865 mpInfrastructureMachinePool *unstructured.Unstructured 866 want []runtimehooksv1.Variable 867 }{ 868 { 869 name: "Should calculate MachinePool variables", 870 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 871 forPatch: "patch1", 872 mpTopology: &clusterv1.MachinePoolTopology{ 873 Replicas: ptr.To[int32](3), 874 Name: "mp-topology", 875 Class: "mp-class", 876 Variables: &clusterv1.MachinePoolVariables{ 877 Overrides: []clusterv1.ClusterVariable{ 878 { 879 Name: "location", 880 Value: toJSON("\"us-central\""), 881 }, 882 { 883 Name: "cpu", 884 Value: toJSON("8"), 885 }, 886 }, 887 }, 888 }, 889 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 890 WithReplicas(3). 891 WithVersion("v1.21.1"). 892 Build(), 893 want: []runtimehooksv1.Variable{ 894 { 895 Name: "location", 896 Value: toJSON("\"us-central\""), 897 }, 898 { 899 Name: "cpu", 900 Value: toJSON("8"), 901 }, 902 { 903 Name: runtimehooksv1.BuiltinsName, 904 Value: toJSONCompact(`{ 905 "machinePool":{ 906 "version": "v1.21.1", 907 "class": "mp-class", 908 "name": "mp1", 909 "topologyName": "mp-topology", 910 "replicas":3 911 }}`), 912 }, 913 }, 914 }, 915 { 916 name: "Should calculate MachinePool variables for a given patch name", 917 forPatch: "patch1", 918 variableDefinitionsForPatch: map[string]bool{ 919 "location": true, 920 "cpu": true, 921 }, 922 mpTopology: &clusterv1.MachinePoolTopology{ 923 Replicas: ptr.To[int32](3), 924 Name: "mp-topology", 925 Class: "mp-class", 926 Variables: &clusterv1.MachinePoolVariables{ 927 Overrides: []clusterv1.ClusterVariable{ 928 { 929 Name: "location", 930 Value: toJSON("\"us-central\""), 931 DefinitionFrom: "patch1", 932 }, 933 { 934 Name: "location", 935 Value: toJSON("\"us-east\""), 936 // This variable should be excluded because it is defined for a different patch. 937 DefinitionFrom: "anotherPatch", 938 }, 939 940 { 941 Name: "http-proxy", 942 Value: toJSON("\"internal.proxy.com\""), 943 // This variable should be excluded because it is not in variableDefinitionsForPatch. 944 DefinitionFrom: "", 945 }, 946 { 947 Name: "cpu", 948 Value: toJSON("8"), 949 // This variable should be included because it is defined for all patches. 950 }, 951 }, 952 }, 953 }, 954 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 955 WithReplicas(3). 956 WithVersion("v1.21.1"). 957 Build(), 958 want: []runtimehooksv1.Variable{ 959 { 960 Name: "location", 961 Value: toJSON("\"us-central\""), 962 }, 963 { 964 Name: "cpu", 965 Value: toJSON("8"), 966 }, 967 { 968 Name: runtimehooksv1.BuiltinsName, 969 Value: toJSONCompact(`{ 970 "machinePool":{ 971 "version": "v1.21.1", 972 "class": "mp-class", 973 "name": "mp1", 974 "topologyName": "mp-topology", 975 "replicas":3 976 }}`), 977 }, 978 }, 979 }, 980 { 981 name: "Should calculate MachinePool variables (without overrides)", 982 forPatch: "patch1", 983 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 984 mpTopology: &clusterv1.MachinePoolTopology{ 985 Replicas: ptr.To[int32](3), 986 Name: "mp-topology", 987 Class: "mp-class", 988 }, 989 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 990 WithReplicas(3). 991 WithVersion("v1.21.1"). 992 Build(), 993 want: []runtimehooksv1.Variable{ 994 { 995 Name: runtimehooksv1.BuiltinsName, 996 Value: toJSONCompact(`{ 997 "machinePool":{ 998 "version": "v1.21.1", 999 "class": "mp-class", 1000 "name": "mp1", 1001 "topologyName": "mp-topology", 1002 "replicas":3 1003 }}`), 1004 }, 1005 }, 1006 }, 1007 { 1008 name: "Should calculate MachinePool variables, replicas not set", 1009 forPatch: "patch1", 1010 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 1011 mpTopology: &clusterv1.MachinePoolTopology{ 1012 Name: "mp-topology", 1013 Class: "mp-class", 1014 Variables: &clusterv1.MachinePoolVariables{ 1015 Overrides: []clusterv1.ClusterVariable{ 1016 { 1017 Name: "location", 1018 Value: toJSON("\"us-central\""), 1019 }, 1020 { 1021 Name: "cpu", 1022 Value: toJSON("8"), 1023 }, 1024 }, 1025 }, 1026 }, 1027 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 1028 WithVersion("v1.21.1"). 1029 Build(), 1030 want: []runtimehooksv1.Variable{ 1031 { 1032 Name: "location", 1033 Value: toJSON("\"us-central\""), 1034 }, 1035 { 1036 Name: "cpu", 1037 Value: toJSON("8"), 1038 }, 1039 { 1040 Name: runtimehooksv1.BuiltinsName, 1041 Value: toJSONCompact(`{ 1042 "machinePool":{ 1043 "version": "v1.21.1", 1044 "class": "mp-class", 1045 "name": "mp1", 1046 "topologyName": "mp-topology" 1047 }}`), 1048 }, 1049 }, 1050 }, 1051 { 1052 name: "Should calculate MachinePool variables with BoostrapConfig", 1053 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 1054 forPatch: "patch1", 1055 mpTopology: &clusterv1.MachinePoolTopology{ 1056 Replicas: ptr.To[int32](3), 1057 Name: "mp-topology", 1058 Class: "mp-class", 1059 Variables: &clusterv1.MachinePoolVariables{ 1060 Overrides: []clusterv1.ClusterVariable{ 1061 { 1062 Name: "location", 1063 Value: toJSON("\"us-central\""), 1064 }, 1065 { 1066 Name: "cpu", 1067 Value: toJSON("8"), 1068 }, 1069 }, 1070 }, 1071 }, 1072 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 1073 WithReplicas(3). 1074 WithVersion("v1.21.1"). 1075 Build(), 1076 mpBootstrapConfig: builder.BootstrapConfig(metav1.NamespaceDefault, "mpBC1").Build(), 1077 want: []runtimehooksv1.Variable{ 1078 { 1079 Name: "location", 1080 Value: toJSON("\"us-central\""), 1081 }, 1082 { 1083 Name: "cpu", 1084 Value: toJSON("8"), 1085 }, 1086 { 1087 Name: runtimehooksv1.BuiltinsName, 1088 Value: toJSONCompact(`{ 1089 "machinePool":{ 1090 "version": "v1.21.1", 1091 "class": "mp-class", 1092 "name": "mp1", 1093 "topologyName": "mp-topology", 1094 "replicas":3, 1095 "bootstrap":{ 1096 "configRef":{ 1097 "name": "mpBC1" 1098 } 1099 } 1100 }}`), 1101 }, 1102 }, 1103 }, 1104 { 1105 name: "Should calculate MachinePool variables with InfrastructureMachinePool", 1106 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 1107 forPatch: "patch1", 1108 mpTopology: &clusterv1.MachinePoolTopology{ 1109 Replicas: ptr.To[int32](3), 1110 Name: "mp-topology", 1111 Class: "mp-class", 1112 Variables: &clusterv1.MachinePoolVariables{ 1113 Overrides: []clusterv1.ClusterVariable{ 1114 { 1115 Name: "location", 1116 Value: toJSON("\"us-central\""), 1117 }, 1118 { 1119 Name: "cpu", 1120 Value: toJSON("8"), 1121 }, 1122 }, 1123 }, 1124 }, 1125 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 1126 WithReplicas(3). 1127 WithVersion("v1.21.1"). 1128 Build(), 1129 mpInfrastructureMachinePool: builder.InfrastructureMachinePool(metav1.NamespaceDefault, "mpIMP1").Build(), 1130 want: []runtimehooksv1.Variable{ 1131 { 1132 Name: "location", 1133 Value: toJSON("\"us-central\""), 1134 }, 1135 { 1136 Name: "cpu", 1137 Value: toJSON("8"), 1138 }, 1139 { 1140 Name: runtimehooksv1.BuiltinsName, 1141 Value: toJSONCompact(`{ 1142 "machinePool":{ 1143 "version": "v1.21.1", 1144 "class": "mp-class", 1145 "name": "mp1", 1146 "topologyName": "mp-topology", 1147 "replicas":3, 1148 "infrastructureRef":{ 1149 "name": "mpIMP1" 1150 } 1151 }}`), 1152 }, 1153 }, 1154 }, 1155 { 1156 name: "Should calculate MachinePool variables with BootstrapConfig and InfrastructureMachinePool", 1157 variableDefinitionsForPatch: map[string]bool{"location": true, "cpu": true}, 1158 forPatch: "patch1", 1159 mpTopology: &clusterv1.MachinePoolTopology{ 1160 Replicas: ptr.To[int32](3), 1161 Name: "mp-topology", 1162 Class: "mp-class", 1163 Variables: &clusterv1.MachinePoolVariables{ 1164 Overrides: []clusterv1.ClusterVariable{ 1165 { 1166 Name: "location", 1167 Value: toJSON("\"us-central\""), 1168 }, 1169 { 1170 Name: "cpu", 1171 Value: toJSON("8"), 1172 }, 1173 }, 1174 }, 1175 }, 1176 mp: builder.MachinePool(metav1.NamespaceDefault, "mp1"). 1177 WithReplicas(3). 1178 WithVersion("v1.21.1"). 1179 Build(), 1180 mpBootstrapConfig: builder.BootstrapConfig(metav1.NamespaceDefault, "mpBC1").Build(), 1181 mpInfrastructureMachinePool: builder.InfrastructureMachinePool(metav1.NamespaceDefault, "mpIMP1").Build(), 1182 want: []runtimehooksv1.Variable{ 1183 { 1184 Name: "location", 1185 Value: toJSON("\"us-central\""), 1186 }, 1187 { 1188 Name: "cpu", 1189 Value: toJSON("8"), 1190 }, 1191 { 1192 Name: runtimehooksv1.BuiltinsName, 1193 Value: toJSONCompact(`{ 1194 "machinePool":{ 1195 "version": "v1.21.1", 1196 "class": "mp-class", 1197 "name": "mp1", 1198 "topologyName": "mp-topology", 1199 "replicas":3, 1200 "bootstrap":{ 1201 "configRef":{ 1202 "name": "mpBC1" 1203 } 1204 }, 1205 "infrastructureRef":{ 1206 "name": "mpIMP1" 1207 } 1208 }}`), 1209 }, 1210 }, 1211 }, 1212 } 1213 for _, tt := range tests { 1214 t.Run(tt.name, func(t *testing.T) { 1215 g := NewWithT(t) 1216 1217 got, err := MachinePool(tt.mpTopology, tt.mp, tt.mpBootstrapConfig, tt.mpInfrastructureMachinePool, tt.forPatch, tt.variableDefinitionsForPatch) 1218 g.Expect(err).ToNot(HaveOccurred()) 1219 g.Expect(got).To(BeComparableTo(tt.want)) 1220 }) 1221 } 1222 } 1223 1224 func toJSON(value string) apiextensionsv1.JSON { 1225 return apiextensionsv1.JSON{Raw: []byte(value)} 1226 } 1227 1228 func toJSONCompact(value string) apiextensionsv1.JSON { 1229 var compactValue bytes.Buffer 1230 if err := json.Compact(&compactValue, []byte(value)); err != nil { 1231 panic(err) 1232 } 1233 return apiextensionsv1.JSON{Raw: compactValue.Bytes()} 1234 }