sigs.k8s.io/cluster-api-provider-azure@v1.17.0/pkg/mutators/azureasomanagedcontrolplane_test.go (about) 1 /* 2 Copyright 2024 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 mutators 18 19 import ( 20 "context" 21 "encoding/json" 22 "testing" 23 24 asocontainerservicev1 "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231001" 25 asocontainerservicev1preview "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231102preview" 26 "github.com/Azure/azure-service-operator/v2/pkg/genruntime" 27 "github.com/google/go-cmp/cmp" 28 . "github.com/onsi/gomega" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/utils/ptr" 33 infrav1alpha "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha1" 34 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 35 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 36 "sigs.k8s.io/cluster-api/util/secret" 37 fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" 38 "sigs.k8s.io/controller-runtime/pkg/conversion" 39 ) 40 41 func TestSetManagedClusterDefaults(t *testing.T) { 42 ctx := context.Background() 43 g := NewGomegaWithT(t) 44 45 tests := []struct { 46 name string 47 asoManagedControlPlane *infrav1alpha.AzureASOManagedControlPlane 48 cluster *clusterv1.Cluster 49 expected []*unstructured.Unstructured 50 expectedErr error 51 }{ 52 { 53 name: "no ManagedCluster", 54 asoManagedControlPlane: &infrav1alpha.AzureASOManagedControlPlane{ 55 Spec: infrav1alpha.AzureASOManagedControlPlaneSpec{ 56 AzureASOManagedControlPlaneTemplateResourceSpec: infrav1alpha.AzureASOManagedControlPlaneTemplateResourceSpec{ 57 Resources: []runtime.RawExtension{}, 58 }, 59 }, 60 }, 61 expectedErr: ErrNoManagedClusterDefined, 62 }, 63 { 64 name: "success", 65 asoManagedControlPlane: &infrav1alpha.AzureASOManagedControlPlane{ 66 Spec: infrav1alpha.AzureASOManagedControlPlaneSpec{ 67 AzureASOManagedControlPlaneTemplateResourceSpec: infrav1alpha.AzureASOManagedControlPlaneTemplateResourceSpec{ 68 Version: "vCAPI k8s version", 69 Resources: []runtime.RawExtension{ 70 { 71 Raw: mcJSON(g, &asocontainerservicev1.ManagedCluster{}), 72 }, 73 }, 74 }, 75 }, 76 }, 77 cluster: &clusterv1.Cluster{ 78 ObjectMeta: metav1.ObjectMeta{ 79 Name: "cluster", 80 }, 81 Spec: clusterv1.ClusterSpec{ 82 ClusterNetwork: &clusterv1.ClusterNetwork{ 83 Pods: &clusterv1.NetworkRanges{ 84 CIDRBlocks: []string{"pod-0", "pod-1"}, 85 }, 86 Services: &clusterv1.NetworkRanges{ 87 CIDRBlocks: []string{"svc-0", "svc-1"}, 88 }, 89 }, 90 }, 91 }, 92 expected: []*unstructured.Unstructured{ 93 mcUnstructured(g, &asocontainerservicev1.ManagedCluster{ 94 Spec: asocontainerservicev1.ManagedCluster_Spec{ 95 KubernetesVersion: ptr.To("CAPI k8s version"), 96 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 97 ServiceCidr: ptr.To("svc-0"), 98 PodCidr: ptr.To("pod-0"), 99 }, 100 OperatorSpec: &asocontainerservicev1.ManagedClusterOperatorSpec{ 101 Secrets: &asocontainerservicev1.ManagedClusterOperatorSecrets{ 102 AdminCredentials: &genruntime.SecretDestination{ 103 Name: secret.Name("cluster", secret.Kubeconfig), 104 Key: secret.KubeconfigDataName, 105 }, 106 }, 107 }, 108 }, 109 }), 110 }, 111 }, 112 } 113 114 for _, test := range tests { 115 t.Run(test.name, func(t *testing.T) { 116 g := NewGomegaWithT(t) 117 118 s := runtime.NewScheme() 119 g.Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 120 g.Expect(infrav1alpha.AddToScheme(s)).To(Succeed()) 121 c := fakeclient.NewClientBuilder(). 122 WithScheme(s). 123 Build() 124 125 mutator := SetManagedClusterDefaults(c, test.asoManagedControlPlane, test.cluster) 126 actual, err := ApplyMutators(ctx, test.asoManagedControlPlane.Spec.Resources, mutator) 127 if test.expectedErr != nil { 128 g.Expect(err).To(MatchError(test.expectedErr)) 129 } else { 130 g.Expect(err).NotTo(HaveOccurred()) 131 } 132 g.Expect(cmp.Diff(test.expected, actual)).To(BeEmpty()) 133 }) 134 } 135 } 136 137 func TestSetManagedClusterKubernetesVersion(t *testing.T) { 138 ctx := context.Background() 139 140 tests := []struct { 141 name string 142 asoManagedControlPlane *infrav1alpha.AzureASOManagedControlPlane 143 managedCluster *asocontainerservicev1.ManagedCluster 144 expected *asocontainerservicev1.ManagedCluster 145 expectedErr error 146 }{ 147 { 148 name: "no CAPI opinion", 149 asoManagedControlPlane: &infrav1alpha.AzureASOManagedControlPlane{}, 150 managedCluster: &asocontainerservicev1.ManagedCluster{ 151 Spec: asocontainerservicev1.ManagedCluster_Spec{ 152 KubernetesVersion: ptr.To("user k8s version"), 153 }, 154 }, 155 expected: &asocontainerservicev1.ManagedCluster{ 156 Spec: asocontainerservicev1.ManagedCluster_Spec{ 157 KubernetesVersion: ptr.To("user k8s version"), 158 }, 159 }, 160 }, 161 { 162 name: "set from CAPI opinion", 163 asoManagedControlPlane: &infrav1alpha.AzureASOManagedControlPlane{ 164 Spec: infrav1alpha.AzureASOManagedControlPlaneSpec{ 165 AzureASOManagedControlPlaneTemplateResourceSpec: infrav1alpha.AzureASOManagedControlPlaneTemplateResourceSpec{ 166 Version: "vCAPI k8s version", 167 }, 168 }, 169 }, 170 managedCluster: &asocontainerservicev1.ManagedCluster{}, 171 expected: &asocontainerservicev1.ManagedCluster{ 172 Spec: asocontainerservicev1.ManagedCluster_Spec{ 173 KubernetesVersion: ptr.To("CAPI k8s version"), 174 }, 175 }, 176 }, 177 { 178 name: "user value matching CAPI ok", 179 asoManagedControlPlane: &infrav1alpha.AzureASOManagedControlPlane{ 180 Spec: infrav1alpha.AzureASOManagedControlPlaneSpec{ 181 AzureASOManagedControlPlaneTemplateResourceSpec: infrav1alpha.AzureASOManagedControlPlaneTemplateResourceSpec{ 182 Version: "vCAPI k8s version", 183 }, 184 }, 185 }, 186 managedCluster: &asocontainerservicev1.ManagedCluster{ 187 Spec: asocontainerservicev1.ManagedCluster_Spec{ 188 KubernetesVersion: ptr.To("CAPI k8s version"), 189 }, 190 }, 191 expected: &asocontainerservicev1.ManagedCluster{ 192 Spec: asocontainerservicev1.ManagedCluster_Spec{ 193 KubernetesVersion: ptr.To("CAPI k8s version"), 194 }, 195 }, 196 }, 197 { 198 name: "incompatible", 199 asoManagedControlPlane: &infrav1alpha.AzureASOManagedControlPlane{ 200 Spec: infrav1alpha.AzureASOManagedControlPlaneSpec{ 201 AzureASOManagedControlPlaneTemplateResourceSpec: infrav1alpha.AzureASOManagedControlPlaneTemplateResourceSpec{ 202 Version: "vCAPI k8s version", 203 }, 204 }, 205 }, 206 managedCluster: &asocontainerservicev1.ManagedCluster{ 207 Spec: asocontainerservicev1.ManagedCluster_Spec{ 208 KubernetesVersion: ptr.To("user k8s version"), 209 }, 210 }, 211 expectedErr: Incompatible{ 212 mutation: mutation{ 213 location: ".spec.kubernetesVersion", 214 val: "CAPI k8s version", 215 reason: "because spec.version is set to vCAPI k8s version", 216 }, 217 userVal: "user k8s version", 218 }, 219 }, 220 } 221 222 s := runtime.NewScheme() 223 NewGomegaWithT(t).Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 224 225 for _, test := range tests { 226 t.Run(test.name, func(t *testing.T) { 227 g := NewGomegaWithT(t) 228 229 before := test.managedCluster.DeepCopy() 230 umc := mcUnstructured(g, test.managedCluster) 231 232 err := setManagedClusterKubernetesVersion(ctx, test.asoManagedControlPlane, "", umc) 233 g.Expect(s.Convert(umc, test.managedCluster, nil)).To(Succeed()) 234 if test.expectedErr != nil { 235 g.Expect(err).To(MatchError(test.expectedErr)) 236 g.Expect(cmp.Diff(before, test.managedCluster)).To(BeEmpty()) // errors should never modify the resource. 237 } else { 238 g.Expect(err).NotTo(HaveOccurred()) 239 g.Expect(cmp.Diff(test.expected, test.managedCluster)).To(BeEmpty()) 240 } 241 }) 242 } 243 } 244 245 func TestSetManagedClusterServiceCIDR(t *testing.T) { 246 ctx := context.Background() 247 248 tests := []struct { 249 name string 250 cluster *clusterv1.Cluster 251 managedCluster *asocontainerservicev1.ManagedCluster 252 expected *asocontainerservicev1.ManagedCluster 253 expectedErr error 254 }{ 255 { 256 name: "no CAPI opinion", 257 cluster: &clusterv1.Cluster{}, 258 managedCluster: &asocontainerservicev1.ManagedCluster{ 259 Spec: asocontainerservicev1.ManagedCluster_Spec{ 260 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 261 ServiceCidr: ptr.To("user cidr"), 262 }, 263 }, 264 }, 265 expected: &asocontainerservicev1.ManagedCluster{ 266 Spec: asocontainerservicev1.ManagedCluster_Spec{ 267 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 268 ServiceCidr: ptr.To("user cidr"), 269 }, 270 }, 271 }, 272 }, 273 { 274 name: "set from CAPI opinion", 275 cluster: &clusterv1.Cluster{ 276 Spec: clusterv1.ClusterSpec{ 277 ClusterNetwork: &clusterv1.ClusterNetwork{ 278 Services: &clusterv1.NetworkRanges{ 279 CIDRBlocks: []string{"capi cidr"}, 280 }, 281 }, 282 }, 283 }, 284 managedCluster: &asocontainerservicev1.ManagedCluster{}, 285 expected: &asocontainerservicev1.ManagedCluster{ 286 Spec: asocontainerservicev1.ManagedCluster_Spec{ 287 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 288 ServiceCidr: ptr.To("capi cidr"), 289 }, 290 }, 291 }, 292 }, 293 { 294 name: "user value matching CAPI ok", 295 cluster: &clusterv1.Cluster{ 296 Spec: clusterv1.ClusterSpec{ 297 ClusterNetwork: &clusterv1.ClusterNetwork{ 298 Services: &clusterv1.NetworkRanges{ 299 CIDRBlocks: []string{"capi cidr"}, 300 }, 301 }, 302 }, 303 }, 304 managedCluster: &asocontainerservicev1.ManagedCluster{ 305 Spec: asocontainerservicev1.ManagedCluster_Spec{ 306 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 307 ServiceCidr: ptr.To("capi cidr"), 308 }, 309 }, 310 }, 311 expected: &asocontainerservicev1.ManagedCluster{ 312 Spec: asocontainerservicev1.ManagedCluster_Spec{ 313 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 314 ServiceCidr: ptr.To("capi cidr"), 315 }, 316 }, 317 }, 318 }, 319 { 320 name: "incompatible", 321 cluster: &clusterv1.Cluster{ 322 ObjectMeta: metav1.ObjectMeta{ 323 Name: "name", 324 Namespace: "ns", 325 }, 326 Spec: clusterv1.ClusterSpec{ 327 ClusterNetwork: &clusterv1.ClusterNetwork{ 328 Services: &clusterv1.NetworkRanges{ 329 CIDRBlocks: []string{"capi cidr"}, 330 }, 331 }, 332 }, 333 }, 334 managedCluster: &asocontainerservicev1.ManagedCluster{ 335 Spec: asocontainerservicev1.ManagedCluster_Spec{ 336 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 337 ServiceCidr: ptr.To("user cidr"), 338 }, 339 }, 340 }, 341 expectedErr: Incompatible{ 342 mutation: mutation{ 343 location: ".spec.networkProfile.serviceCidr", 344 val: "capi cidr", 345 reason: "because spec.clusterNetwork.services.cidrBlocks[0] in Cluster ns/name is set to capi cidr", 346 }, 347 userVal: "user cidr", 348 }, 349 }, 350 } 351 352 s := runtime.NewScheme() 353 NewGomegaWithT(t).Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 354 355 for _, test := range tests { 356 t.Run(test.name, func(t *testing.T) { 357 g := NewGomegaWithT(t) 358 359 before := test.managedCluster.DeepCopy() 360 umc := mcUnstructured(g, test.managedCluster) 361 362 err := setManagedClusterServiceCIDR(ctx, test.cluster, "", umc) 363 g.Expect(s.Convert(umc, test.managedCluster, nil)).To(Succeed()) 364 if test.expectedErr != nil { 365 g.Expect(err).To(MatchError(test.expectedErr)) 366 g.Expect(cmp.Diff(before, test.managedCluster)).To(BeEmpty()) // errors should never modify the resource. 367 } else { 368 g.Expect(err).NotTo(HaveOccurred()) 369 g.Expect(cmp.Diff(test.expected, test.managedCluster)).To(BeEmpty()) 370 } 371 }) 372 } 373 } 374 375 func TestSetManagedClusterPodCIDR(t *testing.T) { 376 ctx := context.Background() 377 378 tests := []struct { 379 name string 380 cluster *clusterv1.Cluster 381 managedCluster *asocontainerservicev1.ManagedCluster 382 expected *asocontainerservicev1.ManagedCluster 383 expectedErr error 384 }{ 385 { 386 name: "no CAPI opinion", 387 cluster: &clusterv1.Cluster{}, 388 managedCluster: &asocontainerservicev1.ManagedCluster{ 389 Spec: asocontainerservicev1.ManagedCluster_Spec{ 390 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 391 PodCidr: ptr.To("user cidr"), 392 }, 393 }, 394 }, 395 expected: &asocontainerservicev1.ManagedCluster{ 396 Spec: asocontainerservicev1.ManagedCluster_Spec{ 397 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 398 PodCidr: ptr.To("user cidr"), 399 }, 400 }, 401 }, 402 }, 403 { 404 name: "set from CAPI opinion", 405 cluster: &clusterv1.Cluster{ 406 Spec: clusterv1.ClusterSpec{ 407 ClusterNetwork: &clusterv1.ClusterNetwork{ 408 Pods: &clusterv1.NetworkRanges{ 409 CIDRBlocks: []string{"capi cidr"}, 410 }, 411 }, 412 }, 413 }, 414 managedCluster: &asocontainerservicev1.ManagedCluster{}, 415 expected: &asocontainerservicev1.ManagedCluster{ 416 Spec: asocontainerservicev1.ManagedCluster_Spec{ 417 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 418 PodCidr: ptr.To("capi cidr"), 419 }, 420 }, 421 }, 422 }, 423 { 424 name: "user value matching CAPI ok", 425 cluster: &clusterv1.Cluster{ 426 Spec: clusterv1.ClusterSpec{ 427 ClusterNetwork: &clusterv1.ClusterNetwork{ 428 Pods: &clusterv1.NetworkRanges{ 429 CIDRBlocks: []string{"capi cidr"}, 430 }, 431 }, 432 }, 433 }, 434 managedCluster: &asocontainerservicev1.ManagedCluster{ 435 Spec: asocontainerservicev1.ManagedCluster_Spec{ 436 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 437 PodCidr: ptr.To("capi cidr"), 438 }, 439 }, 440 }, 441 expected: &asocontainerservicev1.ManagedCluster{ 442 Spec: asocontainerservicev1.ManagedCluster_Spec{ 443 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 444 PodCidr: ptr.To("capi cidr"), 445 }, 446 }, 447 }, 448 }, 449 { 450 name: "incompatible", 451 cluster: &clusterv1.Cluster{ 452 ObjectMeta: metav1.ObjectMeta{ 453 Name: "name", 454 Namespace: "ns", 455 }, 456 Spec: clusterv1.ClusterSpec{ 457 ClusterNetwork: &clusterv1.ClusterNetwork{ 458 Pods: &clusterv1.NetworkRanges{ 459 CIDRBlocks: []string{"capi cidr"}, 460 }, 461 }, 462 }, 463 }, 464 managedCluster: &asocontainerservicev1.ManagedCluster{ 465 Spec: asocontainerservicev1.ManagedCluster_Spec{ 466 NetworkProfile: &asocontainerservicev1.ContainerServiceNetworkProfile{ 467 PodCidr: ptr.To("user cidr"), 468 }, 469 }, 470 }, 471 expectedErr: Incompatible{ 472 mutation: mutation{ 473 location: ".spec.networkProfile.podCidr", 474 val: "capi cidr", 475 reason: "because spec.clusterNetwork.pods.cidrBlocks[0] in Cluster ns/name is set to capi cidr", 476 }, 477 userVal: "user cidr", 478 }, 479 }, 480 } 481 482 s := runtime.NewScheme() 483 NewGomegaWithT(t).Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 484 485 for _, test := range tests { 486 t.Run(test.name, func(t *testing.T) { 487 g := NewGomegaWithT(t) 488 489 before := test.managedCluster.DeepCopy() 490 umc := mcUnstructured(g, test.managedCluster) 491 492 err := setManagedClusterPodCIDR(ctx, test.cluster, "", umc) 493 g.Expect(s.Convert(umc, test.managedCluster, nil)).To(Succeed()) 494 if test.expectedErr != nil { 495 g.Expect(err).To(MatchError(test.expectedErr)) 496 g.Expect(cmp.Diff(before, test.managedCluster)).To(BeEmpty()) // errors should never modify the resource. 497 } else { 498 g.Expect(err).NotTo(HaveOccurred()) 499 g.Expect(cmp.Diff(test.expected, test.managedCluster)).To(BeEmpty()) 500 } 501 }) 502 } 503 } 504 505 func TestSetManagedClusterAgentPoolProfiles(t *testing.T) { 506 g := NewGomegaWithT(t) 507 ctx := context.Background() 508 s := runtime.NewScheme() 509 g.Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 510 g.Expect(infrav1alpha.AddToScheme(s)).To(Succeed()) 511 g.Expect(expv1.AddToScheme(s)).To(Succeed()) 512 fakeClientBuilder := func() *fakeclient.ClientBuilder { 513 return fakeclient.NewClientBuilder().WithScheme(s) 514 } 515 516 t.Run("agent pools should not be defined on user's ManagedCluster", func(t *testing.T) { 517 g := NewGomegaWithT(t) 518 519 umc := mcUnstructured(g, &asocontainerservicev1.ManagedCluster{ 520 Spec: asocontainerservicev1.ManagedCluster_Spec{ 521 AgentPoolProfiles: []asocontainerservicev1.ManagedClusterAgentPoolProfile{{}}, 522 }, 523 }) 524 525 err := setManagedClusterAgentPoolProfiles(ctx, nil, "", nil, "", umc) 526 g.Expect(err).To(MatchError(Incompatible{ 527 mutation: mutation{ 528 location: ".spec.agentPoolProfiles", 529 val: "nil", 530 reason: "because agent pool definitions must be inherited from AzureASOManagedMachinePools", 531 }, 532 userVal: "<slice of length 1>", 533 })) 534 }) 535 536 t.Run("agent pool profiles already created", func(t *testing.T) { 537 g := NewGomegaWithT(t) 538 539 namespace := "ns" 540 managedCluster := &asocontainerservicev1.ManagedCluster{ 541 ObjectMeta: metav1.ObjectMeta{ 542 Name: "mc", 543 Namespace: namespace, 544 }, 545 Status: asocontainerservicev1.ManagedCluster_STATUS{ 546 AgentPoolProfiles: []asocontainerservicev1.ManagedClusterAgentPoolProfile_STATUS{{}}, 547 }, 548 } 549 umc := mcUnstructured(g, managedCluster) 550 551 c := fakeClientBuilder(). 552 WithObjects(managedCluster). 553 Build() 554 555 err := setManagedClusterAgentPoolProfiles(ctx, c, namespace, nil, "", umc) 556 g.Expect(err).NotTo(HaveOccurred()) 557 }) 558 559 t.Run("agent pool profiles derived from managed machine pools", func(t *testing.T) { 560 g := NewGomegaWithT(t) 561 562 namespace := "ns" 563 clusterName := "cluster" 564 managedCluster := &asocontainerservicev1.ManagedCluster{} 565 umc := mcUnstructured(g, managedCluster) 566 567 asoManagedMachinePools := &infrav1alpha.AzureASOManagedMachinePoolList{ 568 Items: []infrav1alpha.AzureASOManagedMachinePool{ 569 { 570 ObjectMeta: metav1.ObjectMeta{ 571 Name: "wrong-label", 572 Namespace: namespace, 573 Labels: map[string]string{ 574 clusterv1.ClusterNameLabel: "not-" + clusterName, 575 }, 576 OwnerReferences: []metav1.OwnerReference{ 577 { 578 APIVersion: expv1.GroupVersion.Identifier(), 579 Kind: "MachinePool", 580 Name: "wrong-label", 581 }, 582 }, 583 }, 584 Spec: infrav1alpha.AzureASOManagedMachinePoolSpec{ 585 AzureASOManagedMachinePoolTemplateResourceSpec: infrav1alpha.AzureASOManagedMachinePoolTemplateResourceSpec{ 586 Resources: []runtime.RawExtension{ 587 { 588 Raw: apJSON(g, &asocontainerservicev1.ManagedClustersAgentPool{ 589 Spec: asocontainerservicev1.ManagedClusters_AgentPool_Spec{ 590 AzureName: "no", 591 }, 592 }), 593 }, 594 }, 595 }, 596 }, 597 }, 598 { 599 ObjectMeta: metav1.ObjectMeta{ 600 Name: "wrong-namespace", 601 Namespace: "not-" + namespace, 602 Labels: map[string]string{ 603 clusterv1.ClusterNameLabel: clusterName, 604 }, 605 OwnerReferences: []metav1.OwnerReference{ 606 { 607 APIVersion: expv1.GroupVersion.Identifier(), 608 Kind: "MachinePool", 609 Name: "wrong-namespace", 610 }, 611 }, 612 }, 613 Spec: infrav1alpha.AzureASOManagedMachinePoolSpec{ 614 AzureASOManagedMachinePoolTemplateResourceSpec: infrav1alpha.AzureASOManagedMachinePoolTemplateResourceSpec{ 615 Resources: []runtime.RawExtension{ 616 { 617 Raw: apJSON(g, &asocontainerservicev1.ManagedClustersAgentPool{ 618 Spec: asocontainerservicev1.ManagedClusters_AgentPool_Spec{ 619 AzureName: "no", 620 }, 621 }), 622 }, 623 }, 624 }, 625 }, 626 }, 627 { 628 ObjectMeta: metav1.ObjectMeta{ 629 Name: "pool0", 630 Namespace: namespace, 631 Labels: map[string]string{ 632 clusterv1.ClusterNameLabel: clusterName, 633 }, 634 OwnerReferences: []metav1.OwnerReference{ 635 { 636 APIVersion: expv1.GroupVersion.Identifier(), 637 Kind: "MachinePool", 638 Name: "pool0", 639 }, 640 }, 641 }, 642 Spec: infrav1alpha.AzureASOManagedMachinePoolSpec{ 643 AzureASOManagedMachinePoolTemplateResourceSpec: infrav1alpha.AzureASOManagedMachinePoolTemplateResourceSpec{ 644 Resources: []runtime.RawExtension{ 645 { 646 Raw: apJSON(g, &asocontainerservicev1.ManagedClustersAgentPool{ 647 Spec: asocontainerservicev1.ManagedClusters_AgentPool_Spec{ 648 AzureName: "azpool0", 649 }, 650 }), 651 }, 652 }, 653 }, 654 }, 655 }, 656 { 657 ObjectMeta: metav1.ObjectMeta{ 658 Name: "pool1", 659 Namespace: namespace, 660 Labels: map[string]string{ 661 clusterv1.ClusterNameLabel: clusterName, 662 }, 663 OwnerReferences: []metav1.OwnerReference{ 664 { 665 APIVersion: expv1.GroupVersion.Identifier(), 666 Kind: "MachinePool", 667 Name: "pool1", 668 }, 669 }, 670 }, 671 Spec: infrav1alpha.AzureASOManagedMachinePoolSpec{ 672 AzureASOManagedMachinePoolTemplateResourceSpec: infrav1alpha.AzureASOManagedMachinePoolTemplateResourceSpec{ 673 Resources: []runtime.RawExtension{ 674 { 675 Raw: apJSON(g, &asocontainerservicev1.ManagedClustersAgentPool{ 676 Spec: asocontainerservicev1.ManagedClusters_AgentPool_Spec{ 677 AzureName: "azpool1", 678 }, 679 }), 680 }, 681 }, 682 }, 683 }, 684 }, 685 }, 686 } 687 machinePools := &expv1.MachinePoolList{ 688 Items: []expv1.MachinePool{ 689 { 690 ObjectMeta: metav1.ObjectMeta{ 691 Namespace: namespace, 692 Name: "wrong-label", 693 }, 694 }, 695 { 696 ObjectMeta: metav1.ObjectMeta{ 697 Namespace: "not-" + namespace, 698 Name: "wrong-namespace", 699 }, 700 }, 701 { 702 ObjectMeta: metav1.ObjectMeta{ 703 Namespace: namespace, 704 Name: "pool0", 705 }, 706 Spec: expv1.MachinePoolSpec{ 707 Replicas: ptr.To[int32](1), 708 }, 709 }, 710 { 711 ObjectMeta: metav1.ObjectMeta{ 712 Namespace: namespace, 713 Name: "pool1", 714 }, 715 Spec: expv1.MachinePoolSpec{ 716 Replicas: ptr.To[int32](2), 717 }, 718 }, 719 }, 720 } 721 expected := &asocontainerservicev1.ManagedCluster{ 722 Spec: asocontainerservicev1.ManagedCluster_Spec{ 723 AgentPoolProfiles: []asocontainerservicev1.ManagedClusterAgentPoolProfile{ 724 {Name: ptr.To("azpool0"), Count: ptr.To(1)}, 725 {Name: ptr.To("azpool1"), Count: ptr.To(2)}, 726 }, 727 }, 728 } 729 730 c := fakeClientBuilder(). 731 WithLists(asoManagedMachinePools, machinePools). 732 Build() 733 734 cluster := &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: clusterName}} 735 err := setManagedClusterAgentPoolProfiles(ctx, c, namespace, cluster, "", umc) 736 g.Expect(err).NotTo(HaveOccurred()) 737 g.Expect(s.Convert(umc, managedCluster, nil)).To(Succeed()) 738 g.Expect(cmp.Diff(expected, managedCluster)).To(BeEmpty()) 739 }) 740 } 741 742 func TestSetAgentPoolProfilesFromAgentPools(t *testing.T) { 743 t.Run("stable with no pools", func(t *testing.T) { 744 g := NewGomegaWithT(t) 745 746 mc := &asocontainerservicev1.ManagedCluster{} 747 var pools []conversion.Convertible 748 var expected []asocontainerservicev1.ManagedClusterAgentPoolProfile 749 750 err := setAgentPoolProfilesFromAgentPools(mc, pools) 751 g.Expect(err).NotTo(HaveOccurred()) 752 g.Expect(cmp.Diff(expected, mc.Spec.AgentPoolProfiles)).To(BeEmpty()) 753 }) 754 755 t.Run("stable with pools", func(t *testing.T) { 756 g := NewGomegaWithT(t) 757 758 mc := &asocontainerservicev1.ManagedCluster{} 759 pools := []conversion.Convertible{ 760 &asocontainerservicev1.ManagedClustersAgentPool{ 761 Spec: asocontainerservicev1.ManagedClusters_AgentPool_Spec{ 762 AzureName: "pool0", 763 MaxCount: ptr.To(1), 764 }, 765 }, 766 // Not all pools have to be the same version, or the same version as the cluster. 767 &asocontainerservicev1preview.ManagedClustersAgentPool{ 768 Spec: asocontainerservicev1preview.ManagedClusters_AgentPool_Spec{ 769 AzureName: "pool1", 770 MinCount: ptr.To(2), 771 EnableCustomCATrust: ptr.To(true), 772 }, 773 }, 774 } 775 expected := []asocontainerservicev1.ManagedClusterAgentPoolProfile{ 776 { 777 Name: ptr.To("pool0"), 778 MaxCount: ptr.To(1), 779 }, 780 { 781 Name: ptr.To("pool1"), 782 MinCount: ptr.To(2), 783 // EnableCustomCATrust is a preview-only feature that can't be represented here, so it should be lost. 784 }, 785 } 786 787 err := setAgentPoolProfilesFromAgentPools(mc, pools) 788 g.Expect(err).NotTo(HaveOccurred()) 789 g.Expect(cmp.Diff(expected, mc.Spec.AgentPoolProfiles)).To(BeEmpty()) 790 }) 791 792 t.Run("preview with pools", func(t *testing.T) { 793 g := NewGomegaWithT(t) 794 795 mc := &asocontainerservicev1preview.ManagedCluster{} 796 pools := []conversion.Convertible{ 797 &asocontainerservicev1.ManagedClustersAgentPool{ 798 Spec: asocontainerservicev1.ManagedClusters_AgentPool_Spec{ 799 AzureName: "pool0", 800 MaxCount: ptr.To(1), 801 }, 802 }, 803 &asocontainerservicev1preview.ManagedClustersAgentPool{ 804 Spec: asocontainerservicev1preview.ManagedClusters_AgentPool_Spec{ 805 AzureName: "pool1", 806 MinCount: ptr.To(2), 807 EnableCustomCATrust: ptr.To(true), 808 }, 809 }, 810 } 811 expected := []asocontainerservicev1preview.ManagedClusterAgentPoolProfile{ 812 { 813 Name: ptr.To("pool0"), 814 MaxCount: ptr.To(1), 815 }, 816 { 817 Name: ptr.To("pool1"), 818 MinCount: ptr.To(2), 819 EnableCustomCATrust: ptr.To(true), 820 }, 821 } 822 823 err := setAgentPoolProfilesFromAgentPools(mc, pools) 824 g.Expect(err).NotTo(HaveOccurred()) 825 g.Expect(cmp.Diff(expected, mc.Spec.AgentPoolProfiles)).To(BeEmpty()) 826 }) 827 } 828 829 func mcJSON(g Gomega, mc *asocontainerservicev1.ManagedCluster) []byte { 830 mc.SetGroupVersionKind(asocontainerservicev1.GroupVersion.WithKind("ManagedCluster")) 831 j, err := json.Marshal(mc) 832 g.Expect(err).NotTo(HaveOccurred()) 833 return j 834 } 835 836 func apJSON(g Gomega, mc *asocontainerservicev1.ManagedClustersAgentPool) []byte { 837 mc.SetGroupVersionKind(asocontainerservicev1.GroupVersion.WithKind("ManagedClustersAgentPool")) 838 j, err := json.Marshal(mc) 839 g.Expect(err).NotTo(HaveOccurred()) 840 return j 841 } 842 843 func mcUnstructured(g Gomega, mc *asocontainerservicev1.ManagedCluster) *unstructured.Unstructured { 844 s := runtime.NewScheme() 845 g.Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 846 u := &unstructured.Unstructured{} 847 g.Expect(s.Convert(mc, u, nil)).To(Succeed()) 848 return u 849 } 850 851 func apUnstructured(g Gomega, ap *asocontainerservicev1.ManagedClustersAgentPool) *unstructured.Unstructured { 852 s := runtime.NewScheme() 853 g.Expect(asocontainerservicev1.AddToScheme(s)).To(Succeed()) 854 u := &unstructured.Unstructured{} 855 g.Expect(s.Convert(ap, u, nil)).To(Succeed()) 856 return u 857 }