sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/scope/cluster_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 scope 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "strings" 24 "testing" 25 26 asonetworkv1api20201101 "github.com/Azure/azure-service-operator/v2/api/network/v1api20201101" 27 asonetworkv1api20220701 "github.com/Azure/azure-service-operator/v2/api/network/v1api20220701" 28 asoresourcesv1 "github.com/Azure/azure-service-operator/v2/api/resources/v1api20200601" 29 "github.com/Azure/go-autorest/autorest/azure/auth" 30 "github.com/google/go-cmp/cmp" 31 . "github.com/onsi/gomega" 32 corev1 "k8s.io/api/core/v1" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/runtime" 35 "k8s.io/utils/ptr" 36 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 37 "sigs.k8s.io/cluster-api-provider-azure/azure" 38 "sigs.k8s.io/cluster-api-provider-azure/azure/services/bastionhosts" 39 "sigs.k8s.io/cluster-api-provider-azure/azure/services/groups" 40 "sigs.k8s.io/cluster-api-provider-azure/azure/services/loadbalancers" 41 "sigs.k8s.io/cluster-api-provider-azure/azure/services/natgateways" 42 "sigs.k8s.io/cluster-api-provider-azure/azure/services/privateendpoints" 43 "sigs.k8s.io/cluster-api-provider-azure/azure/services/publicips" 44 "sigs.k8s.io/cluster-api-provider-azure/azure/services/routetables" 45 "sigs.k8s.io/cluster-api-provider-azure/azure/services/securitygroups" 46 "sigs.k8s.io/cluster-api-provider-azure/azure/services/subnets" 47 "sigs.k8s.io/cluster-api-provider-azure/azure/services/vnetpeerings" 48 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 49 "sigs.k8s.io/controller-runtime/pkg/client/fake" 50 ) 51 52 const fakeClientID = "fake-client-id" 53 const fakeTenantID = "fake-tenant-id" 54 55 func specToString(spec any) string { 56 var sb strings.Builder 57 sb.WriteString("{ ") 58 sb.WriteString(fmt.Sprintf("%+v ", spec)) 59 sb.WriteString("}") 60 return sb.String() 61 } 62 63 func specArrayToString[T any](specs []T) string { 64 var sb strings.Builder 65 sb.WriteString("[\n") 66 for _, spec := range specs { 67 sb.WriteString(fmt.Sprintf("\t%+v\n", specToString(spec))) 68 } 69 sb.WriteString("]") 70 71 return sb.String() 72 } 73 74 func TestAPIServerHost(t *testing.T) { 75 fakeSubscriptionID := "123" 76 77 tests := []struct { 78 name string 79 azureCluster infrav1.AzureCluster 80 want string 81 }{ 82 { 83 name: "public apiserver lb (user-defined dns)", 84 azureCluster: infrav1.AzureCluster{ 85 Spec: infrav1.AzureClusterSpec{ 86 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 87 SubscriptionID: fakeSubscriptionID, 88 IdentityRef: &corev1.ObjectReference{ 89 Kind: infrav1.AzureClusterIdentityKind, 90 }, 91 }, 92 NetworkSpec: infrav1.NetworkSpec{ 93 APIServerLB: infrav1.LoadBalancerSpec{ 94 FrontendIPs: []infrav1.FrontendIP{ 95 { 96 PublicIP: &infrav1.PublicIPSpec{ 97 DNSName: "my-cluster-apiserver.example.com", 98 }, 99 }, 100 }, 101 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 102 Type: infrav1.Public, 103 }, 104 }, 105 }, 106 }, 107 }, 108 want: "my-cluster-apiserver.example.com", 109 }, 110 { 111 name: "private apiserver lb (default private dns zone)", 112 azureCluster: infrav1.AzureCluster{ 113 Spec: infrav1.AzureClusterSpec{ 114 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 115 SubscriptionID: fakeSubscriptionID, 116 IdentityRef: &corev1.ObjectReference{ 117 Kind: infrav1.AzureClusterIdentityKind, 118 }, 119 }, 120 NetworkSpec: infrav1.NetworkSpec{ 121 APIServerLB: infrav1.LoadBalancerSpec{ 122 FrontendIPs: []infrav1.FrontendIP{ 123 { 124 PublicIP: &infrav1.PublicIPSpec{ 125 DNSName: "my-cluster-apiserver.capz.io", 126 }, 127 }, 128 }, 129 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 130 Type: infrav1.Public, 131 }, 132 }, 133 }, 134 }, 135 }, 136 want: "my-cluster-apiserver.capz.io", 137 }, 138 { 139 name: "private apiserver (user-defined private dns zone)", 140 azureCluster: infrav1.AzureCluster{ 141 Spec: infrav1.AzureClusterSpec{ 142 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 143 SubscriptionID: fakeSubscriptionID, 144 IdentityRef: &corev1.ObjectReference{ 145 Kind: infrav1.AzureClusterIdentityKind, 146 }, 147 }, 148 NetworkSpec: infrav1.NetworkSpec{ 149 NetworkClassSpec: infrav1.NetworkClassSpec{ 150 PrivateDNSZoneName: "example.private", 151 }, 152 APIServerLB: infrav1.LoadBalancerSpec{ 153 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 154 Type: infrav1.Internal, 155 }, 156 }, 157 }, 158 }, 159 }, 160 want: "apiserver.example.private", 161 }, 162 } 163 164 for _, tc := range tests { 165 tc := tc 166 g := NewWithT(t) 167 scheme := runtime.NewScheme() 168 _ = clusterv1.AddToScheme(scheme) 169 _ = infrav1.AddToScheme(scheme) 170 _ = corev1.AddToScheme(scheme) 171 172 cluster := &clusterv1.Cluster{ 173 ObjectMeta: metav1.ObjectMeta{ 174 Name: "my-cluster", 175 Namespace: "default", 176 }, 177 } 178 179 tc.azureCluster.ObjectMeta = metav1.ObjectMeta{ 180 Name: cluster.Name, 181 OwnerReferences: []metav1.OwnerReference{ 182 { 183 APIVersion: "cluster.x-k8s.io/v1beta1", 184 Kind: "Cluster", 185 Name: "my-cluster", 186 }, 187 }, 188 } 189 tc.azureCluster.Default() 190 191 fakeIdentity := &infrav1.AzureClusterIdentity{ 192 Spec: infrav1.AzureClusterIdentitySpec{ 193 Type: infrav1.ServicePrincipal, 194 ClientID: fakeClientID, 195 TenantID: fakeTenantID, 196 }, 197 } 198 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 199 200 initObjects := []runtime.Object{cluster, &tc.azureCluster, fakeIdentity, fakeSecret} 201 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 202 203 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 204 Cluster: cluster, 205 AzureCluster: &tc.azureCluster, 206 Client: fakeClient, 207 }) 208 g.Expect(err).NotTo(HaveOccurred()) 209 210 g.Expect(clusterScope.APIServerHost()).Should(Equal(tc.want)) 211 } 212 } 213 214 func TestGettingSecurityRules(t *testing.T) { 215 g := NewWithT(t) 216 scheme := runtime.NewScheme() 217 _ = clusterv1.AddToScheme(scheme) 218 _ = infrav1.AddToScheme(scheme) 219 _ = corev1.AddToScheme(scheme) 220 221 cluster := &clusterv1.Cluster{ 222 ObjectMeta: metav1.ObjectMeta{ 223 Name: "my-cluster", 224 Namespace: "default", 225 }, 226 } 227 228 azureCluster := &infrav1.AzureCluster{ 229 ObjectMeta: metav1.ObjectMeta{ 230 Name: "my-azure-cluster", 231 OwnerReferences: []metav1.OwnerReference{ 232 { 233 APIVersion: "cluster.x-k8s.io/v1beta1", 234 Kind: "Cluster", 235 Name: "my-cluster", 236 }, 237 }, 238 }, 239 Spec: infrav1.AzureClusterSpec{ 240 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 241 SubscriptionID: "123", 242 IdentityRef: &corev1.ObjectReference{ 243 Kind: infrav1.AzureClusterIdentityKind, 244 }, 245 }, 246 NetworkSpec: infrav1.NetworkSpec{ 247 Subnets: infrav1.Subnets{ 248 { 249 SubnetClassSpec: infrav1.SubnetClassSpec{ 250 Role: infrav1.SubnetNode, 251 Name: "node", 252 }, 253 }, 254 }, 255 }, 256 }, 257 } 258 azureCluster.Default() 259 260 fakeIdentity := &infrav1.AzureClusterIdentity{ 261 Spec: infrav1.AzureClusterIdentitySpec{ 262 Type: infrav1.ServicePrincipal, 263 ClientID: fakeClientID, 264 TenantID: fakeTenantID, 265 }, 266 } 267 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 268 269 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 270 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 271 272 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 273 Cluster: cluster, 274 AzureCluster: azureCluster, 275 Client: fakeClient, 276 }) 277 g.Expect(err).NotTo(HaveOccurred()) 278 279 clusterScope.SetControlPlaneSecurityRules() 280 281 subnet, err := clusterScope.AzureCluster.Spec.NetworkSpec.GetControlPlaneSubnet() 282 g.Expect(err).NotTo(HaveOccurred()) 283 g.Expect(subnet.SecurityGroup.SecurityRules).To(HaveLen(2)) 284 } 285 286 func TestPublicIPSpecs(t *testing.T) { 287 tests := []struct { 288 name string 289 azureCluster *infrav1.AzureCluster 290 expectedPublicIPSpec []azure.ResourceSpecGetter 291 }{ 292 { 293 name: "Azure cluster with internal type LB and nil frontend IP count", 294 azureCluster: &infrav1.AzureCluster{ 295 ObjectMeta: metav1.ObjectMeta{ 296 Name: "my-cluster", 297 OwnerReferences: []metav1.OwnerReference{ 298 { 299 APIVersion: "cluster.x-k8s.io/v1beta1", 300 Kind: "Cluster", 301 Name: "my-cluster", 302 }, 303 }, 304 }, 305 Status: infrav1.AzureClusterStatus{ 306 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 307 "failure-domain-id-1": {}, 308 "failure-domain-id-2": {}, 309 "failure-domain-id-3": {}, 310 }, 311 }, 312 Spec: infrav1.AzureClusterSpec{ 313 ResourceGroup: "my-rg", 314 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 315 SubscriptionID: "123", 316 Location: "centralIndia", 317 AdditionalTags: infrav1.Tags{ 318 "Name": "my-publicip-ipv6", 319 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 320 }, 321 IdentityRef: &corev1.ObjectReference{ 322 Kind: infrav1.AzureClusterIdentityKind, 323 }, 324 }, 325 NetworkSpec: infrav1.NetworkSpec{ 326 APIServerLB: infrav1.LoadBalancerSpec{ 327 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 328 Type: infrav1.Internal, 329 }, 330 }, 331 }, 332 }, 333 }, 334 expectedPublicIPSpec: nil, 335 }, 336 { 337 name: "Azure cluster with internal type LB and 0 frontend IP count", 338 azureCluster: &infrav1.AzureCluster{ 339 ObjectMeta: metav1.ObjectMeta{ 340 Name: "my-cluster", 341 OwnerReferences: []metav1.OwnerReference{ 342 { 343 APIVersion: "cluster.x-k8s.io/v1beta1", 344 Kind: "Cluster", 345 Name: "my-cluster", 346 }, 347 }, 348 }, 349 Status: infrav1.AzureClusterStatus{ 350 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 351 "failure-domain-id-1": {}, 352 "failure-domain-id-2": {}, 353 "failure-domain-id-3": {}, 354 }, 355 }, 356 Spec: infrav1.AzureClusterSpec{ 357 ResourceGroup: "my-rg", 358 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 359 SubscriptionID: "123", 360 Location: "centralIndia", 361 AdditionalTags: infrav1.Tags{ 362 "Name": "my-publicip-ipv6", 363 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 364 }, 365 IdentityRef: &corev1.ObjectReference{ 366 Kind: infrav1.AzureClusterIdentityKind, 367 }, 368 }, 369 NetworkSpec: infrav1.NetworkSpec{ 370 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 371 FrontendIPsCount: ptr.To[int32](0), 372 }, 373 APIServerLB: infrav1.LoadBalancerSpec{ 374 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 375 Type: infrav1.Internal, 376 }, 377 }, 378 }, 379 }, 380 }, 381 expectedPublicIPSpec: nil, 382 }, 383 { 384 name: "Azure cluster with internal type apiserver LB and 1 frontend IP count", 385 azureCluster: &infrav1.AzureCluster{ 386 ObjectMeta: metav1.ObjectMeta{ 387 Name: "my-cluster", 388 OwnerReferences: []metav1.OwnerReference{ 389 { 390 APIVersion: "cluster.x-k8s.io/v1beta1", 391 Kind: "Cluster", 392 Name: "my-cluster", 393 }, 394 }, 395 }, 396 Status: infrav1.AzureClusterStatus{ 397 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 398 "failure-domain-id-1": {}, 399 "failure-domain-id-2": {}, 400 "failure-domain-id-3": {}, 401 }, 402 }, 403 Spec: infrav1.AzureClusterSpec{ 404 ResourceGroup: "my-rg", 405 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 406 SubscriptionID: "123", 407 Location: "centralIndia", 408 AdditionalTags: infrav1.Tags{ 409 "Name": "my-publicip-ipv6", 410 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 411 }, 412 IdentityRef: &corev1.ObjectReference{ 413 Kind: infrav1.AzureClusterIdentityKind, 414 }, 415 }, 416 NetworkSpec: infrav1.NetworkSpec{ 417 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 418 FrontendIPsCount: ptr.To[int32](1), 419 FrontendIPs: []infrav1.FrontendIP{ 420 { 421 Name: "my-frontend-ip", 422 PublicIP: &infrav1.PublicIPSpec{ 423 Name: "pip-my-cluster-controlplane-outbound", 424 }, 425 }, 426 }, 427 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 428 }, 429 APIServerLB: infrav1.LoadBalancerSpec{ 430 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 431 Type: infrav1.Internal, 432 }, 433 }, 434 }, 435 }, 436 }, 437 expectedPublicIPSpec: []azure.ResourceSpecGetter{ 438 &publicips.PublicIPSpec{ 439 Name: "pip-my-cluster-controlplane-outbound", 440 ResourceGroup: "my-rg", 441 DNSName: "", 442 IsIPv6: false, 443 ClusterName: "my-cluster", 444 Location: "centralIndia", 445 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 446 AdditionalTags: infrav1.Tags{ 447 "Name": "my-publicip-ipv6", 448 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 449 }, 450 }, 451 }, 452 }, 453 { 454 name: "Azure cluster with internal type apiserver LB and many frontend IP count", 455 azureCluster: &infrav1.AzureCluster{ 456 ObjectMeta: metav1.ObjectMeta{ 457 Name: "my-cluster", 458 OwnerReferences: []metav1.OwnerReference{ 459 { 460 APIVersion: "cluster.x-k8s.io/v1beta1", 461 Kind: "Cluster", 462 Name: "my-cluster", 463 }, 464 }, 465 }, 466 Status: infrav1.AzureClusterStatus{ 467 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 468 "failure-domain-id-1": {}, 469 "failure-domain-id-2": {}, 470 "failure-domain-id-3": {}, 471 }, 472 }, 473 Spec: infrav1.AzureClusterSpec{ 474 ResourceGroup: "my-rg", 475 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 476 SubscriptionID: "123", 477 Location: "centralIndia", 478 AdditionalTags: infrav1.Tags{ 479 "Name": "my-publicip-ipv6", 480 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 481 }, 482 IdentityRef: &corev1.ObjectReference{ 483 Kind: infrav1.AzureClusterIdentityKind, 484 }, 485 }, 486 NetworkSpec: infrav1.NetworkSpec{ 487 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 488 FrontendIPsCount: ptr.To[int32](3), 489 FrontendIPs: []infrav1.FrontendIP{ 490 { 491 Name: "my-frontend-ip-1", 492 PublicIP: &infrav1.PublicIPSpec{ 493 Name: "pip-my-cluster-controlplane-outbound-1", 494 }, 495 }, 496 { 497 Name: "my-frontend-ip-2", 498 PublicIP: &infrav1.PublicIPSpec{ 499 Name: "pip-my-cluster-controlplane-outbound-2", 500 }, 501 }, 502 { 503 Name: "my-frontend-ip-3", 504 PublicIP: &infrav1.PublicIPSpec{ 505 Name: "pip-my-cluster-controlplane-outbound-3", 506 }, 507 }, 508 }, 509 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 510 }, 511 APIServerLB: infrav1.LoadBalancerSpec{ 512 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 513 Type: infrav1.Internal, 514 }, 515 }, 516 }, 517 }, 518 }, 519 expectedPublicIPSpec: []azure.ResourceSpecGetter{ 520 &publicips.PublicIPSpec{ 521 Name: "pip-my-cluster-controlplane-outbound-1", 522 ResourceGroup: "my-rg", 523 DNSName: "", 524 IsIPv6: false, 525 ClusterName: "my-cluster", 526 Location: "centralIndia", 527 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 528 AdditionalTags: infrav1.Tags{ 529 "Name": "my-publicip-ipv6", 530 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 531 }, 532 }, 533 &publicips.PublicIPSpec{ 534 Name: "pip-my-cluster-controlplane-outbound-2", 535 ResourceGroup: "my-rg", 536 DNSName: "", 537 IsIPv6: false, 538 ClusterName: "my-cluster", 539 Location: "centralIndia", 540 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 541 AdditionalTags: infrav1.Tags{ 542 "Name": "my-publicip-ipv6", 543 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 544 }, 545 }, 546 &publicips.PublicIPSpec{ 547 Name: "pip-my-cluster-controlplane-outbound-3", 548 ResourceGroup: "my-rg", 549 DNSName: "", 550 IsIPv6: false, 551 ClusterName: "my-cluster", 552 Location: "centralIndia", 553 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 554 AdditionalTags: infrav1.Tags{ 555 "Name": "my-publicip-ipv6", 556 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 557 }, 558 }, 559 }, 560 }, 561 { 562 name: "Azure cluster with public type apiserver LB", 563 azureCluster: &infrav1.AzureCluster{ 564 ObjectMeta: metav1.ObjectMeta{ 565 Name: "my-cluster", 566 OwnerReferences: []metav1.OwnerReference{ 567 { 568 APIVersion: "cluster.x-k8s.io/v1beta1", 569 Kind: "Cluster", 570 Name: "my-cluster", 571 }, 572 }, 573 }, 574 Status: infrav1.AzureClusterStatus{ 575 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 576 "failure-domain-id-1": {}, 577 "failure-domain-id-2": {}, 578 "failure-domain-id-3": {}, 579 }, 580 }, 581 Spec: infrav1.AzureClusterSpec{ 582 ResourceGroup: "my-rg", 583 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 584 SubscriptionID: "123", 585 Location: "centralIndia", 586 AdditionalTags: infrav1.Tags{ 587 "Name": "my-publicip-ipv6", 588 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 589 }, 590 IdentityRef: &corev1.ObjectReference{ 591 Kind: infrav1.AzureClusterIdentityKind, 592 }, 593 }, 594 NetworkSpec: infrav1.NetworkSpec{ 595 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 596 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 597 }, 598 APIServerLB: infrav1.LoadBalancerSpec{ 599 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 600 FrontendIPs: []infrav1.FrontendIP{ 601 { 602 PublicIP: &infrav1.PublicIPSpec{ 603 Name: "40.60.89.22", 604 DNSName: "fake-dns", 605 }, 606 }, 607 }, 608 }, 609 }, 610 }, 611 }, 612 expectedPublicIPSpec: []azure.ResourceSpecGetter{ 613 &publicips.PublicIPSpec{ 614 Name: "40.60.89.22", 615 ResourceGroup: "my-rg", 616 DNSName: "fake-dns", 617 IsIPv6: false, 618 ClusterName: "my-cluster", 619 Location: "centralIndia", 620 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 621 AdditionalTags: infrav1.Tags{ 622 "Name": "my-publicip-ipv6", 623 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 624 }, 625 }, 626 }, 627 }, 628 { 629 name: "Azure cluster with public type apiserver LB and public node outbound lb", 630 azureCluster: &infrav1.AzureCluster{ 631 ObjectMeta: metav1.ObjectMeta{ 632 Name: "my-cluster", 633 OwnerReferences: []metav1.OwnerReference{ 634 { 635 APIVersion: "cluster.x-k8s.io/v1beta1", 636 Kind: "Cluster", 637 Name: "my-cluster", 638 }, 639 }, 640 }, 641 Status: infrav1.AzureClusterStatus{ 642 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 643 "failure-domain-id-1": {}, 644 "failure-domain-id-2": {}, 645 "failure-domain-id-3": {}, 646 }, 647 }, 648 Spec: infrav1.AzureClusterSpec{ 649 ResourceGroup: "my-rg", 650 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 651 SubscriptionID: "123", 652 Location: "centralIndia", 653 AdditionalTags: infrav1.Tags{ 654 "Name": "my-publicip-ipv6", 655 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 656 }, 657 IdentityRef: &corev1.ObjectReference{ 658 Kind: infrav1.AzureClusterIdentityKind, 659 }, 660 }, 661 NetworkSpec: infrav1.NetworkSpec{ 662 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 663 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 664 }, 665 NodeOutboundLB: &infrav1.LoadBalancerSpec{ 666 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 667 }, 668 APIServerLB: infrav1.LoadBalancerSpec{ 669 FrontendIPs: []infrav1.FrontendIP{ 670 { 671 PublicIP: &infrav1.PublicIPSpec{ 672 Name: "40.60.89.22", 673 DNSName: "fake-dns", 674 }, 675 }, 676 }, 677 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 678 }, 679 }, 680 }, 681 }, 682 expectedPublicIPSpec: []azure.ResourceSpecGetter{ 683 &publicips.PublicIPSpec{ 684 Name: "40.60.89.22", 685 ResourceGroup: "my-rg", 686 DNSName: "fake-dns", 687 IsIPv6: false, 688 ClusterName: "my-cluster", 689 Location: "centralIndia", 690 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 691 AdditionalTags: infrav1.Tags{ 692 "Name": "my-publicip-ipv6", 693 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 694 }, 695 }, 696 }, 697 }, 698 { 699 name: "Azure cluster with public type apiserver LB and public node outbound lb, NAT gateways and bastions", 700 azureCluster: &infrav1.AzureCluster{ 701 ObjectMeta: metav1.ObjectMeta{ 702 Name: "my-cluster", 703 OwnerReferences: []metav1.OwnerReference{ 704 { 705 APIVersion: "cluster.x-k8s.io/v1beta1", 706 Kind: "Cluster", 707 Name: "my-cluster", 708 }, 709 }, 710 }, 711 Status: infrav1.AzureClusterStatus{ 712 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 713 "failure-domain-id-1": {}, 714 "failure-domain-id-2": {}, 715 "failure-domain-id-3": {}, 716 }, 717 }, 718 Spec: infrav1.AzureClusterSpec{ 719 ResourceGroup: "my-rg", 720 BastionSpec: infrav1.BastionSpec{ 721 AzureBastion: &infrav1.AzureBastion{ 722 PublicIP: infrav1.PublicIPSpec{ 723 Name: "fake-bastion-public-ip", 724 DNSName: "fake-bastion-dns-name", 725 }, 726 }, 727 }, 728 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 729 SubscriptionID: "123", 730 Location: "centralIndia", 731 AdditionalTags: infrav1.Tags{ 732 "Name": "my-publicip-ipv6", 733 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 734 }, 735 IdentityRef: &corev1.ObjectReference{ 736 Kind: infrav1.AzureClusterIdentityKind, 737 }, 738 }, 739 NetworkSpec: infrav1.NetworkSpec{ 740 Subnets: infrav1.Subnets{ 741 infrav1.SubnetSpec{ 742 SubnetClassSpec: infrav1.SubnetClassSpec{ 743 Role: infrav1.SubnetNode, 744 }, 745 NatGateway: infrav1.NatGateway{ 746 NatGatewayIP: infrav1.PublicIPSpec{ 747 Name: "fake-public-ip", 748 DNSName: "fake-dns-name", 749 }, 750 }, 751 }, 752 }, 753 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 754 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 755 }, 756 NodeOutboundLB: &infrav1.LoadBalancerSpec{ 757 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 758 }, 759 APIServerLB: infrav1.LoadBalancerSpec{ 760 FrontendIPs: []infrav1.FrontendIP{ 761 { 762 PublicIP: &infrav1.PublicIPSpec{ 763 Name: "40.60.89.22", 764 DNSName: "fake-dns", 765 }, 766 }, 767 }, 768 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{}, 769 }, 770 }, 771 }, 772 }, 773 expectedPublicIPSpec: []azure.ResourceSpecGetter{ 774 &publicips.PublicIPSpec{ 775 Name: "40.60.89.22", 776 ResourceGroup: "my-rg", 777 DNSName: "fake-dns", 778 IsIPv6: false, 779 ClusterName: "my-cluster", 780 Location: "centralIndia", 781 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 782 AdditionalTags: infrav1.Tags{ 783 "Name": "my-publicip-ipv6", 784 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 785 }, 786 }, 787 &publicips.PublicIPSpec{ 788 Name: "fake-bastion-public-ip", 789 ResourceGroup: "my-rg", 790 DNSName: "fake-bastion-dns-name", 791 IsIPv6: false, 792 ClusterName: "my-cluster", 793 Location: "centralIndia", 794 FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 795 AdditionalTags: infrav1.Tags{ 796 "Name": "my-publicip-ipv6", 797 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 798 }, 799 }, 800 }, 801 }, 802 } 803 804 for _, tc := range tests { 805 t.Run(tc.name, func(t *testing.T) { 806 g := NewWithT(t) 807 scheme := runtime.NewScheme() 808 _ = infrav1.AddToScheme(scheme) 809 _ = clusterv1.AddToScheme(scheme) 810 _ = corev1.AddToScheme(scheme) 811 812 cluster := &clusterv1.Cluster{ 813 ObjectMeta: metav1.ObjectMeta{ 814 Name: tc.azureCluster.Name, 815 Namespace: "default", 816 }, 817 } 818 fakeIdentity := &infrav1.AzureClusterIdentity{ 819 Spec: infrav1.AzureClusterIdentitySpec{ 820 Type: infrav1.ServicePrincipal, 821 ClientID: fakeClientID, 822 TenantID: fakeTenantID, 823 }, 824 } 825 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 826 827 initObjects := []runtime.Object{cluster, tc.azureCluster, fakeIdentity, fakeSecret} 828 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 829 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 830 Cluster: cluster, 831 AzureCluster: tc.azureCluster, 832 Client: fakeClient, 833 }) 834 g.Expect(err).NotTo(HaveOccurred()) 835 836 if got := clusterScope.PublicIPSpecs(); !reflect.DeepEqual(got, tc.expectedPublicIPSpec) { 837 t.Errorf("PublicIPSpecs() diff between expected result and actual result (%v): %s", got, cmp.Diff(tc.expectedPublicIPSpec, got)) 838 } 839 }) 840 } 841 } 842 843 func TestRouteTableSpecs(t *testing.T) { 844 tests := []struct { 845 name string 846 clusterScope ClusterScope 847 want []azure.ResourceSpecGetter 848 }{ 849 { 850 name: "returns nil if no subnets are specified", 851 clusterScope: ClusterScope{ 852 AzureCluster: &infrav1.AzureCluster{ 853 Spec: infrav1.AzureClusterSpec{ 854 NetworkSpec: infrav1.NetworkSpec{ 855 Subnets: infrav1.Subnets{}, 856 }, 857 }, 858 }, 859 cache: &ClusterCache{}, 860 }, 861 want: nil, 862 }, 863 { 864 name: "returns specified route tables if present", 865 clusterScope: ClusterScope{ 866 Cluster: &clusterv1.Cluster{ 867 ObjectMeta: metav1.ObjectMeta{ 868 Name: "my-cluster", 869 }, 870 }, 871 AzureCluster: &infrav1.AzureCluster{ 872 Spec: infrav1.AzureClusterSpec{ 873 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 874 Location: "centralIndia", 875 IdentityRef: &corev1.ObjectReference{ 876 Kind: infrav1.AzureClusterIdentityKind, 877 }, 878 }, 879 NetworkSpec: infrav1.NetworkSpec{ 880 Vnet: infrav1.VnetSpec{ 881 ResourceGroup: "my-rg", 882 }, 883 Subnets: infrav1.Subnets{ 884 { 885 RouteTable: infrav1.RouteTable{ 886 ID: "fake-route-table-id-1", 887 Name: "fake-route-table-1", 888 }, 889 }, 890 { 891 RouteTable: infrav1.RouteTable{ 892 ID: "fake-route-table-id-2", 893 Name: "fake-route-table-2", 894 }, 895 }, 896 }, 897 }, 898 }, 899 }, 900 cache: &ClusterCache{}, 901 }, 902 want: []azure.ResourceSpecGetter{ 903 &routetables.RouteTableSpec{ 904 Name: "fake-route-table-1", 905 ResourceGroup: "my-rg", 906 Location: "centralIndia", 907 ClusterName: "my-cluster", 908 AdditionalTags: make(infrav1.Tags), 909 }, 910 &routetables.RouteTableSpec{ 911 Name: "fake-route-table-2", 912 ResourceGroup: "my-rg", 913 Location: "centralIndia", 914 ClusterName: "my-cluster", 915 AdditionalTags: make(infrav1.Tags), 916 }, 917 }, 918 }, 919 } 920 921 for _, tt := range tests { 922 tt := tt 923 t.Run(tt.name, func(t *testing.T) { 924 t.Parallel() 925 if got := tt.clusterScope.RouteTableSpecs(); !reflect.DeepEqual(got, tt.want) { 926 t.Errorf("RouteTableSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want)) 927 } 928 }) 929 } 930 } 931 932 func TestNatGatewaySpecs(t *testing.T) { 933 tests := []struct { 934 name string 935 clusterScope ClusterScope 936 want []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway] 937 }{ 938 { 939 name: "returns nil if no subnets are specified", 940 clusterScope: ClusterScope{ 941 AzureCluster: &infrav1.AzureCluster{ 942 Spec: infrav1.AzureClusterSpec{ 943 NetworkSpec: infrav1.NetworkSpec{ 944 Subnets: infrav1.Subnets{}, 945 }, 946 }, 947 }, 948 cache: &ClusterCache{}, 949 }, 950 want: nil, 951 }, 952 { 953 name: "returns specified node NAT gateway if present", 954 clusterScope: ClusterScope{ 955 Cluster: &clusterv1.Cluster{ 956 ObjectMeta: metav1.ObjectMeta{ 957 Name: "my-cluster", 958 }, 959 }, 960 AzureClients: AzureClients{ 961 EnvironmentSettings: auth.EnvironmentSettings{ 962 Values: map[string]string{ 963 auth.SubscriptionID: "123", 964 }, 965 }, 966 }, 967 AzureCluster: &infrav1.AzureCluster{ 968 Spec: infrav1.AzureClusterSpec{ 969 ResourceGroup: "my-rg", 970 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 971 Location: "centralIndia", 972 IdentityRef: &corev1.ObjectReference{ 973 Kind: infrav1.AzureClusterIdentityKind, 974 }, 975 }, 976 NetworkSpec: infrav1.NetworkSpec{ 977 Subnets: infrav1.Subnets{ 978 { 979 SubnetClassSpec: infrav1.SubnetClassSpec{ 980 Role: infrav1.SubnetNode, 981 }, 982 RouteTable: infrav1.RouteTable{ 983 ID: "fake-route-table-id-1", 984 Name: "fake-route-table-1", 985 }, 986 NatGateway: infrav1.NatGateway{ 987 NatGatewayIP: infrav1.PublicIPSpec{ 988 Name: "44.78.67.90", 989 }, 990 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 991 Name: "fake-nat-gateway-1", 992 }, 993 }, 994 }, 995 }, 996 }, 997 }, 998 }, 999 cache: &ClusterCache{}, 1000 }, 1001 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{ 1002 &natgateways.NatGatewaySpec{ 1003 Name: "fake-nat-gateway-1", 1004 ResourceGroup: "my-rg", 1005 Location: "centralIndia", 1006 SubscriptionID: "123", 1007 ClusterName: "my-cluster", 1008 NatGatewayIP: infrav1.PublicIPSpec{ 1009 Name: "44.78.67.90", 1010 }, 1011 AdditionalTags: make(infrav1.Tags), 1012 IsVnetManaged: true, 1013 }, 1014 }, 1015 }, 1016 { 1017 name: "returns specified node NAT gateway if present and ignores duplicate", 1018 clusterScope: ClusterScope{ 1019 Cluster: &clusterv1.Cluster{ 1020 ObjectMeta: metav1.ObjectMeta{ 1021 Name: "my-cluster", 1022 }, 1023 }, 1024 AzureClients: AzureClients{ 1025 EnvironmentSettings: auth.EnvironmentSettings{ 1026 Values: map[string]string{ 1027 auth.SubscriptionID: "123", 1028 }, 1029 }, 1030 }, 1031 AzureCluster: &infrav1.AzureCluster{ 1032 Spec: infrav1.AzureClusterSpec{ 1033 ResourceGroup: "my-rg", 1034 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1035 Location: "centralIndia", 1036 IdentityRef: &corev1.ObjectReference{ 1037 Kind: infrav1.AzureClusterIdentityKind, 1038 }, 1039 }, 1040 NetworkSpec: infrav1.NetworkSpec{ 1041 Subnets: infrav1.Subnets{ 1042 { 1043 SubnetClassSpec: infrav1.SubnetClassSpec{ 1044 Role: infrav1.SubnetNode, 1045 }, 1046 RouteTable: infrav1.RouteTable{ 1047 ID: "fake-route-table-id-1", 1048 Name: "fake-route-table-1", 1049 }, 1050 NatGateway: infrav1.NatGateway{ 1051 NatGatewayIP: infrav1.PublicIPSpec{ 1052 Name: "44.78.67.90", 1053 }, 1054 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1055 Name: "fake-nat-gateway-1", 1056 }, 1057 }, 1058 }, 1059 // Duplicate Entry 1060 { 1061 SubnetClassSpec: infrav1.SubnetClassSpec{ 1062 Role: infrav1.SubnetNode, 1063 }, 1064 RouteTable: infrav1.RouteTable{ 1065 ID: "fake-route-table-id-1", 1066 Name: "fake-route-table-1", 1067 }, 1068 NatGateway: infrav1.NatGateway{ 1069 NatGatewayIP: infrav1.PublicIPSpec{ 1070 Name: "44.78.67.90", 1071 }, 1072 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1073 Name: "fake-nat-gateway-1", 1074 }, 1075 }, 1076 }, 1077 }, 1078 }, 1079 }, 1080 }, 1081 cache: &ClusterCache{}, 1082 }, 1083 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{ 1084 &natgateways.NatGatewaySpec{ 1085 Name: "fake-nat-gateway-1", 1086 ResourceGroup: "my-rg", 1087 Location: "centralIndia", 1088 SubscriptionID: "123", 1089 ClusterName: "my-cluster", 1090 NatGatewayIP: infrav1.PublicIPSpec{ 1091 Name: "44.78.67.90", 1092 }, 1093 AdditionalTags: make(infrav1.Tags), 1094 IsVnetManaged: true, 1095 }, 1096 }, 1097 }, 1098 { 1099 name: "returns specified node NAT gateway if present and ignores control plane nat gateway", 1100 clusterScope: ClusterScope{ 1101 Cluster: &clusterv1.Cluster{ 1102 ObjectMeta: metav1.ObjectMeta{ 1103 Name: "my-cluster", 1104 }, 1105 }, 1106 AzureClients: AzureClients{ 1107 EnvironmentSettings: auth.EnvironmentSettings{ 1108 Values: map[string]string{ 1109 auth.SubscriptionID: "123", 1110 }, 1111 }, 1112 }, 1113 AzureCluster: &infrav1.AzureCluster{ 1114 Spec: infrav1.AzureClusterSpec{ 1115 ResourceGroup: "my-rg", 1116 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1117 Location: "centralIndia", 1118 IdentityRef: &corev1.ObjectReference{ 1119 Kind: infrav1.AzureClusterIdentityKind, 1120 }, 1121 }, 1122 NetworkSpec: infrav1.NetworkSpec{ 1123 Subnets: infrav1.Subnets{ 1124 { 1125 SubnetClassSpec: infrav1.SubnetClassSpec{ 1126 Role: infrav1.SubnetNode, 1127 }, 1128 RouteTable: infrav1.RouteTable{ 1129 ID: "fake-route-table-id-1", 1130 Name: "fake-route-table-1", 1131 }, 1132 NatGateway: infrav1.NatGateway{ 1133 NatGatewayIP: infrav1.PublicIPSpec{ 1134 Name: "44.78.67.90", 1135 }, 1136 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1137 Name: "fake-nat-gateway-1", 1138 }, 1139 }, 1140 }, 1141 { 1142 SubnetClassSpec: infrav1.SubnetClassSpec{ 1143 Role: infrav1.SubnetControlPlane, 1144 }, 1145 RouteTable: infrav1.RouteTable{ 1146 ID: "fake-route-table-id-2", 1147 Name: "fake-route-table-2", 1148 }, 1149 NatGateway: infrav1.NatGateway{ 1150 NatGatewayIP: infrav1.PublicIPSpec{ 1151 Name: "44.78.67.91", 1152 }, 1153 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1154 Name: "fake-nat-gateway-2", 1155 }, 1156 }, 1157 }, 1158 }, 1159 }, 1160 }, 1161 }, 1162 cache: &ClusterCache{}, 1163 }, 1164 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{ 1165 &natgateways.NatGatewaySpec{ 1166 Name: "fake-nat-gateway-1", 1167 ResourceGroup: "my-rg", 1168 Location: "centralIndia", 1169 SubscriptionID: "123", 1170 ClusterName: "my-cluster", 1171 NatGatewayIP: infrav1.PublicIPSpec{ 1172 Name: "44.78.67.90", 1173 }, 1174 AdditionalTags: make(infrav1.Tags), 1175 IsVnetManaged: true, 1176 }, 1177 }, 1178 }, 1179 } 1180 1181 for _, tt := range tests { 1182 tt := tt 1183 t.Run(tt.name, func(t *testing.T) { 1184 t.Parallel() 1185 if got := tt.clusterScope.NatGatewaySpecs(); !reflect.DeepEqual(got, tt.want) { 1186 t.Errorf("NatGatewaySpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want)) 1187 } 1188 }) 1189 } 1190 } 1191 1192 func TestSetNatGatewayIDInSubnets(t *testing.T) { 1193 tests := []struct { 1194 name string 1195 clusterScope ClusterScope 1196 asoNatgateway *asonetworkv1api20220701.NatGateway 1197 }{ 1198 { 1199 name: "sets nat gateway id in the matching subnet", 1200 clusterScope: ClusterScope{ 1201 Cluster: &clusterv1.Cluster{ 1202 ObjectMeta: metav1.ObjectMeta{ 1203 Name: "my-cluster", 1204 }, 1205 }, 1206 AzureCluster: &infrav1.AzureCluster{ 1207 Spec: infrav1.AzureClusterSpec{ 1208 NetworkSpec: infrav1.NetworkSpec{ 1209 Subnets: infrav1.Subnets{ 1210 { 1211 SubnetClassSpec: infrav1.SubnetClassSpec{ 1212 Name: "fake-subnet-1", 1213 }, 1214 NatGateway: infrav1.NatGateway{ 1215 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1216 Name: "fake-nat-gateway-1", 1217 }, 1218 }, 1219 }, 1220 { 1221 SubnetClassSpec: infrav1.SubnetClassSpec{ 1222 Name: "fake-subnet-2", 1223 }, 1224 NatGateway: infrav1.NatGateway{ 1225 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1226 Name: "fake-nat-gateway-2", 1227 }, 1228 }, 1229 }, 1230 }, 1231 }, 1232 }, 1233 }, 1234 cache: &ClusterCache{}, 1235 }, 1236 asoNatgateway: &asonetworkv1api20220701.NatGateway{ 1237 ObjectMeta: metav1.ObjectMeta{ 1238 Name: "fake-nat-gateway-1", 1239 }, 1240 Status: asonetworkv1api20220701.NatGateway_STATUS{ 1241 Id: ptr.To("dummy-id-1"), 1242 }, 1243 }, 1244 }, 1245 } 1246 1247 for _, tt := range tests { 1248 tt := tt 1249 t.Run(tt.name, func(t *testing.T) { 1250 g := NewWithT(t) 1251 t.Parallel() 1252 tt.clusterScope.SetNatGatewayIDInSubnets(tt.asoNatgateway.Name, *tt.asoNatgateway.Status.Id) 1253 for _, subnet := range tt.clusterScope.AzureCluster.Spec.NetworkSpec.Subnets { 1254 if subnet.NatGateway.Name == tt.asoNatgateway.Name { 1255 g.Expect(subnet.NatGateway.ID).To(Equal(*tt.asoNatgateway.Status.Id)) 1256 } else { 1257 g.Expect(subnet.NatGateway.ID).To(Equal("")) 1258 } 1259 } 1260 }) 1261 } 1262 } 1263 1264 func TestNSGSpecs(t *testing.T) { 1265 tests := []struct { 1266 name string 1267 clusterScope ClusterScope 1268 want []azure.ResourceSpecGetter 1269 }{ 1270 { 1271 name: "returns empty if no subnets are specified", 1272 clusterScope: ClusterScope{ 1273 AzureCluster: &infrav1.AzureCluster{ 1274 Spec: infrav1.AzureClusterSpec{ 1275 NetworkSpec: infrav1.NetworkSpec{ 1276 Subnets: infrav1.Subnets{}, 1277 }, 1278 }, 1279 }, 1280 }, 1281 want: []azure.ResourceSpecGetter{}, 1282 }, 1283 { 1284 name: "returns specified security groups if present", 1285 clusterScope: ClusterScope{ 1286 Cluster: &clusterv1.Cluster{ 1287 ObjectMeta: metav1.ObjectMeta{ 1288 Name: "my-cluster", 1289 }, 1290 }, 1291 AzureCluster: &infrav1.AzureCluster{ 1292 Spec: infrav1.AzureClusterSpec{ 1293 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1294 Location: "centralIndia", 1295 IdentityRef: &corev1.ObjectReference{ 1296 Kind: infrav1.AzureClusterIdentityKind, 1297 }, 1298 }, 1299 NetworkSpec: infrav1.NetworkSpec{ 1300 Vnet: infrav1.VnetSpec{ 1301 ResourceGroup: "my-rg", 1302 }, 1303 Subnets: infrav1.Subnets{ 1304 { 1305 SecurityGroup: infrav1.SecurityGroup{ 1306 Name: "fake-security-group-1", 1307 SecurityGroupClass: infrav1.SecurityGroupClass{ 1308 SecurityRules: infrav1.SecurityRules{ 1309 { 1310 Name: "fake-rule-1", 1311 }, 1312 }, 1313 }, 1314 }, 1315 }, 1316 }, 1317 }, 1318 }, 1319 }, 1320 cache: &ClusterCache{}, 1321 }, 1322 want: []azure.ResourceSpecGetter{ 1323 &securitygroups.NSGSpec{ 1324 Name: "fake-security-group-1", 1325 SecurityRules: infrav1.SecurityRules{ 1326 { 1327 Name: "fake-rule-1", 1328 }, 1329 }, 1330 ResourceGroup: "my-rg", 1331 Location: "centralIndia", 1332 ClusterName: "my-cluster", 1333 AdditionalTags: make(infrav1.Tags), 1334 LastAppliedSecurityRules: map[string]interface{}{}, 1335 }, 1336 }, 1337 }, 1338 } 1339 1340 for _, tt := range tests { 1341 tt := tt 1342 t.Run(tt.name, func(t *testing.T) { 1343 t.Parallel() 1344 if got := tt.clusterScope.NSGSpecs(); !reflect.DeepEqual(got, tt.want) { 1345 t.Errorf("RouteTableSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want)) 1346 } 1347 }) 1348 } 1349 } 1350 1351 func TestSubnetSpecs(t *testing.T) { 1352 tests := []struct { 1353 name string 1354 clusterScope ClusterScope 1355 want []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet] 1356 }{ 1357 { 1358 name: "returns empty if no subnets are specified", 1359 clusterScope: ClusterScope{ 1360 AzureCluster: &infrav1.AzureCluster{ 1361 Spec: infrav1.AzureClusterSpec{ 1362 NetworkSpec: infrav1.NetworkSpec{ 1363 Subnets: infrav1.Subnets{}, 1364 }, 1365 }, 1366 }, 1367 cache: &ClusterCache{}, 1368 }, 1369 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{}, 1370 }, 1371 { 1372 name: "returns specified subnet spec", 1373 clusterScope: ClusterScope{ 1374 Cluster: &clusterv1.Cluster{ 1375 ObjectMeta: metav1.ObjectMeta{ 1376 Name: "my-cluster", 1377 }, 1378 }, 1379 AzureClients: AzureClients{ 1380 EnvironmentSettings: auth.EnvironmentSettings{ 1381 Values: map[string]string{ 1382 auth.SubscriptionID: "123", 1383 }, 1384 }, 1385 }, 1386 AzureCluster: &infrav1.AzureCluster{ 1387 Spec: infrav1.AzureClusterSpec{ 1388 ResourceGroup: "my-rg", 1389 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1390 Location: "centralIndia", 1391 IdentityRef: &corev1.ObjectReference{ 1392 Kind: infrav1.AzureClusterIdentityKind, 1393 }, 1394 }, 1395 NetworkSpec: infrav1.NetworkSpec{ 1396 Vnet: infrav1.VnetSpec{ 1397 ID: "fake-vnet-id-1", 1398 Name: "fake-vnet-1", 1399 ResourceGroup: "my-rg-vnet", 1400 }, 1401 Subnets: infrav1.Subnets{ 1402 { 1403 SubnetClassSpec: infrav1.SubnetClassSpec{ 1404 Role: infrav1.SubnetNode, 1405 CIDRBlocks: []string{"192.168.1.1/16"}, 1406 Name: "fake-subnet-1", 1407 }, 1408 NatGateway: infrav1.NatGateway{ 1409 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1410 Name: "fake-natgateway-1", 1411 }, 1412 }, 1413 RouteTable: infrav1.RouteTable{ 1414 ID: "fake-route-table-id-1", 1415 Name: "fake-route-table-1", 1416 }, 1417 SecurityGroup: infrav1.SecurityGroup{ 1418 Name: "fake-security-group-1", 1419 SecurityGroupClass: infrav1.SecurityGroupClass{ 1420 SecurityRules: infrav1.SecurityRules{ 1421 { 1422 Name: "fake-rule-1", 1423 }, 1424 }, 1425 }, 1426 }, 1427 }, 1428 }, 1429 }, 1430 }, 1431 }, 1432 cache: &ClusterCache{}, 1433 }, 1434 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{ 1435 &subnets.SubnetSpec{ 1436 Name: "fake-subnet-1", 1437 ResourceGroup: "my-rg", 1438 SubscriptionID: "123", 1439 CIDRs: []string{"192.168.1.1/16"}, 1440 VNetName: "fake-vnet-1", 1441 VNetResourceGroup: "my-rg-vnet", 1442 IsVNetManaged: false, 1443 RouteTableName: "fake-route-table-1", 1444 SecurityGroupName: "fake-security-group-1", 1445 NatGatewayName: "fake-natgateway-1", 1446 }, 1447 }, 1448 }, 1449 1450 { 1451 name: "returns specified subnet spec and bastion spec if enabled", 1452 clusterScope: ClusterScope{ 1453 Cluster: &clusterv1.Cluster{ 1454 ObjectMeta: metav1.ObjectMeta{ 1455 Name: "my-cluster", 1456 }, 1457 }, 1458 AzureClients: AzureClients{ 1459 EnvironmentSettings: auth.EnvironmentSettings{ 1460 Values: map[string]string{ 1461 auth.SubscriptionID: "123", 1462 }, 1463 }, 1464 }, 1465 AzureCluster: &infrav1.AzureCluster{ 1466 Spec: infrav1.AzureClusterSpec{ 1467 BastionSpec: infrav1.BastionSpec{ 1468 AzureBastion: &infrav1.AzureBastion{ 1469 Name: "fake-azure-bastion", 1470 Subnet: infrav1.SubnetSpec{ 1471 SubnetClassSpec: infrav1.SubnetClassSpec{ 1472 Role: infrav1.SubnetBastion, 1473 CIDRBlocks: []string{"172.122.1.1./16"}, 1474 Name: "fake-bastion-subnet-1", 1475 }, 1476 RouteTable: infrav1.RouteTable{ 1477 ID: "fake-bastion-route-table-id-1", 1478 Name: "fake-bastion-route-table-1", 1479 }, 1480 SecurityGroup: infrav1.SecurityGroup{ 1481 Name: "fake-bastion-security-group-1", 1482 SecurityGroupClass: infrav1.SecurityGroupClass{ 1483 SecurityRules: infrav1.SecurityRules{ 1484 { 1485 Name: "fake-rule-1", 1486 }, 1487 }, 1488 }, 1489 }, 1490 }, 1491 }, 1492 }, 1493 ResourceGroup: "my-rg", 1494 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1495 Location: "centralIndia", 1496 IdentityRef: &corev1.ObjectReference{ 1497 Kind: infrav1.AzureClusterIdentityKind, 1498 }, 1499 }, 1500 NetworkSpec: infrav1.NetworkSpec{ 1501 Vnet: infrav1.VnetSpec{ 1502 ID: "fake-vnet-id-1", 1503 Name: "fake-vnet-1", 1504 ResourceGroup: "my-rg-vnet", 1505 }, 1506 Subnets: infrav1.Subnets{ 1507 { 1508 SubnetClassSpec: infrav1.SubnetClassSpec{ 1509 Role: infrav1.SubnetNode, 1510 CIDRBlocks: []string{"192.168.1.1/16"}, 1511 Name: "fake-subnet-1", 1512 }, 1513 NatGateway: infrav1.NatGateway{ 1514 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1515 Name: "fake-natgateway-1", 1516 }, 1517 }, 1518 RouteTable: infrav1.RouteTable{ 1519 ID: "fake-route-table-id-1", 1520 Name: "fake-route-table-1", 1521 }, 1522 SecurityGroup: infrav1.SecurityGroup{ 1523 Name: "fake-security-group-1", 1524 SecurityGroupClass: infrav1.SecurityGroupClass{ 1525 SecurityRules: infrav1.SecurityRules{ 1526 { 1527 Name: "fake-rule-1", 1528 }, 1529 }, 1530 }, 1531 }, 1532 }, 1533 }, 1534 }, 1535 }, 1536 }, 1537 cache: &ClusterCache{}, 1538 }, 1539 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{ 1540 &subnets.SubnetSpec{ 1541 Name: "fake-subnet-1", 1542 ResourceGroup: "my-rg", 1543 SubscriptionID: "123", 1544 CIDRs: []string{"192.168.1.1/16"}, 1545 VNetName: "fake-vnet-1", 1546 VNetResourceGroup: "my-rg-vnet", 1547 IsVNetManaged: false, 1548 RouteTableName: "fake-route-table-1", 1549 SecurityGroupName: "fake-security-group-1", 1550 NatGatewayName: "fake-natgateway-1", 1551 }, 1552 &subnets.SubnetSpec{ 1553 Name: "fake-bastion-subnet-1", 1554 ResourceGroup: "my-rg", 1555 SubscriptionID: "123", 1556 CIDRs: []string{"172.122.1.1./16"}, 1557 VNetName: "fake-vnet-1", 1558 VNetResourceGroup: "my-rg-vnet", 1559 IsVNetManaged: false, 1560 SecurityGroupName: "fake-bastion-security-group-1", 1561 RouteTableName: "fake-bastion-route-table-1", 1562 }, 1563 }, 1564 }, 1565 } 1566 1567 for _, tt := range tests { 1568 tt := tt 1569 t.Run(tt.name, func(t *testing.T) { 1570 t.Parallel() 1571 if got := tt.clusterScope.SubnetSpecs(); !reflect.DeepEqual(got, tt.want) { 1572 t.Errorf("SubnetSpecs() = \n%s, want \n%s", specArrayToString(got), specArrayToString(tt.want)) 1573 } 1574 }) 1575 } 1576 } 1577 1578 func TestIsVnetManaged(t *testing.T) { 1579 tests := []struct { 1580 name string 1581 clusterScope ClusterScope 1582 want bool 1583 }{ 1584 { 1585 name: "VNET ID is empty", 1586 clusterScope: ClusterScope{ 1587 Cluster: &clusterv1.Cluster{ 1588 ObjectMeta: metav1.ObjectMeta{ 1589 Name: "my-cluster", 1590 }, 1591 }, 1592 AzureCluster: &infrav1.AzureCluster{ 1593 Spec: infrav1.AzureClusterSpec{ 1594 NetworkSpec: infrav1.NetworkSpec{ 1595 Vnet: infrav1.VnetSpec{ 1596 ID: "", 1597 }, 1598 }, 1599 }, 1600 }, 1601 cache: &ClusterCache{}, 1602 }, 1603 want: true, 1604 }, 1605 { 1606 name: "Wrong tags", 1607 clusterScope: ClusterScope{ 1608 Cluster: &clusterv1.Cluster{ 1609 ObjectMeta: metav1.ObjectMeta{ 1610 Name: "my-cluster", 1611 }, 1612 }, 1613 AzureCluster: &infrav1.AzureCluster{ 1614 Spec: infrav1.AzureClusterSpec{ 1615 NetworkSpec: infrav1.NetworkSpec{ 1616 Vnet: infrav1.VnetSpec{ 1617 ID: "my-id", 1618 VnetClassSpec: infrav1.VnetClassSpec{Tags: map[string]string{ 1619 "key": "value", 1620 }}, 1621 }, 1622 }, 1623 }, 1624 }, 1625 cache: &ClusterCache{}, 1626 }, 1627 want: false, 1628 }, 1629 { 1630 name: "Has owning tags", 1631 clusterScope: ClusterScope{ 1632 Cluster: &clusterv1.Cluster{ 1633 ObjectMeta: metav1.ObjectMeta{ 1634 Name: "my-cluster", 1635 }, 1636 }, 1637 AzureCluster: &infrav1.AzureCluster{ 1638 Spec: infrav1.AzureClusterSpec{ 1639 NetworkSpec: infrav1.NetworkSpec{ 1640 Vnet: infrav1.VnetSpec{ 1641 ID: "my-id", 1642 VnetClassSpec: infrav1.VnetClassSpec{Tags: map[string]string{ 1643 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned", 1644 }}, 1645 }, 1646 }, 1647 }, 1648 }, 1649 cache: &ClusterCache{}, 1650 }, 1651 want: true, 1652 }, 1653 { 1654 name: "Has cached value of false", 1655 clusterScope: ClusterScope{ 1656 AzureCluster: &infrav1.AzureCluster{ 1657 Spec: infrav1.AzureClusterSpec{}, 1658 }, 1659 cache: &ClusterCache{ 1660 isVnetManaged: ptr.To(false), 1661 }, 1662 }, 1663 want: false, 1664 }, 1665 { 1666 name: "Has cached value of true", 1667 clusterScope: ClusterScope{ 1668 AzureCluster: &infrav1.AzureCluster{ 1669 Spec: infrav1.AzureClusterSpec{}, 1670 }, 1671 cache: &ClusterCache{ 1672 isVnetManaged: ptr.To(true), 1673 }, 1674 }, 1675 want: true, 1676 }, 1677 } 1678 1679 for _, tt := range tests { 1680 tt := tt 1681 t.Run(tt.name, func(t *testing.T) { 1682 t.Parallel() 1683 got := tt.clusterScope.IsVnetManaged() 1684 if !reflect.DeepEqual(got, tt.want) { 1685 t.Errorf("IsVnetManaged() = \n%t, want \n%t", got, tt.want) 1686 } 1687 if ptr.Deref(tt.clusterScope.cache.isVnetManaged, false) != got { 1688 t.Errorf("IsVnetManaged() = \n%t, cache = \n%t", got, ptr.Deref(tt.clusterScope.cache.isVnetManaged, false)) 1689 } 1690 }) 1691 } 1692 } 1693 1694 func TestAzureBastionSpec(t *testing.T) { 1695 tests := []struct { 1696 name string 1697 clusterScope ClusterScope 1698 want azure.ASOResourceSpecGetter[*asonetworkv1api20220701.BastionHost] 1699 }{ 1700 { 1701 name: "returns nil if no subnets are specified", 1702 clusterScope: ClusterScope{ 1703 AzureCluster: &infrav1.AzureCluster{ 1704 Spec: infrav1.AzureClusterSpec{ 1705 NetworkSpec: infrav1.NetworkSpec{ 1706 Subnets: infrav1.Subnets{}, 1707 }, 1708 }, 1709 }, 1710 }, 1711 want: nil, 1712 }, 1713 { 1714 name: "returns bastion spec if enabled", 1715 clusterScope: ClusterScope{ 1716 Cluster: &clusterv1.Cluster{ 1717 ObjectMeta: metav1.ObjectMeta{ 1718 Name: "my-cluster", 1719 }, 1720 }, 1721 AzureClients: AzureClients{ 1722 EnvironmentSettings: auth.EnvironmentSettings{ 1723 Values: map[string]string{ 1724 auth.SubscriptionID: "123", 1725 }, 1726 }, 1727 }, 1728 AzureCluster: &infrav1.AzureCluster{ 1729 Spec: infrav1.AzureClusterSpec{ 1730 BastionSpec: infrav1.BastionSpec{ 1731 AzureBastion: &infrav1.AzureBastion{ 1732 Name: "fake-azure-bastion-1", 1733 Subnet: infrav1.SubnetSpec{ 1734 SubnetClassSpec: infrav1.SubnetClassSpec{ 1735 Role: infrav1.SubnetBastion, 1736 CIDRBlocks: []string{"172.122.1.1./16"}, 1737 Name: "fake-bastion-subnet-1", 1738 }, 1739 RouteTable: infrav1.RouteTable{ 1740 ID: "fake-bastion-route-table-id-1", 1741 Name: "fake-bastion-route-table-1", 1742 }, 1743 SecurityGroup: infrav1.SecurityGroup{ 1744 Name: "fake-bastion-security-group-1", 1745 SecurityGroupClass: infrav1.SecurityGroupClass{ 1746 SecurityRules: infrav1.SecurityRules{ 1747 { 1748 Name: "fake-rule-1", 1749 }, 1750 }, 1751 }, 1752 }, 1753 }, 1754 PublicIP: infrav1.PublicIPSpec{ 1755 Name: "fake-public-ip-1", 1756 }, 1757 }, 1758 }, 1759 ResourceGroup: "my-rg", 1760 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1761 Location: "centralIndia", 1762 IdentityRef: &corev1.ObjectReference{ 1763 Kind: infrav1.AzureClusterIdentityKind, 1764 }, 1765 }, 1766 NetworkSpec: infrav1.NetworkSpec{ 1767 Vnet: infrav1.VnetSpec{ 1768 ID: "fake-vnet-id-1", 1769 Name: "fake-vnet-1", 1770 ResourceGroup: "my-rg-vnet", 1771 }, 1772 Subnets: infrav1.Subnets{ 1773 { 1774 SubnetClassSpec: infrav1.SubnetClassSpec{ 1775 Role: infrav1.SubnetNode, 1776 CIDRBlocks: []string{"192.168.1.1/16"}, 1777 Name: "fake-subnet-1", 1778 }, 1779 NatGateway: infrav1.NatGateway{ 1780 NatGatewayClassSpec: infrav1.NatGatewayClassSpec{ 1781 Name: "fake-natgateway-1", 1782 }, 1783 }, 1784 RouteTable: infrav1.RouteTable{ 1785 ID: "fake-route-table-id-1", 1786 Name: "fake-route-table-1", 1787 }, 1788 SecurityGroup: infrav1.SecurityGroup{ 1789 Name: "fake-security-group-1", 1790 SecurityGroupClass: infrav1.SecurityGroupClass{ 1791 SecurityRules: infrav1.SecurityRules{ 1792 { 1793 Name: "fake-rule-1", 1794 }, 1795 }, 1796 }, 1797 }, 1798 }, 1799 }, 1800 }, 1801 }, 1802 }, 1803 cache: &ClusterCache{}, 1804 }, 1805 want: &bastionhosts.AzureBastionSpec{ 1806 Name: "fake-azure-bastion-1", 1807 ResourceGroup: "my-rg", 1808 Location: "centralIndia", 1809 ClusterName: "my-cluster", 1810 SubnetID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/"+ 1811 "virtualNetworks/%s/subnets/%s", "123", "my-rg", "fake-vnet-1", "fake-bastion-subnet-1"), 1812 PublicIPID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/"+ 1813 "publicIPAddresses/%s", "123", "my-rg", "fake-public-ip-1"), 1814 }, 1815 }, 1816 } 1817 1818 for _, tt := range tests { 1819 tt := tt 1820 t.Run(tt.name, func(t *testing.T) { 1821 t.Parallel() 1822 if got := tt.clusterScope.AzureBastionSpec(); !reflect.DeepEqual(got, tt.want) { 1823 t.Errorf("AzureBastionSpec() = \n%s, want \n%s", specToString(got), specToString(tt.want)) 1824 } 1825 }) 1826 } 1827 } 1828 1829 func TestSubnet(t *testing.T) { 1830 tests := []struct { 1831 clusterName string 1832 subnetName string 1833 azureClusterNetworkSpec infrav1.NetworkSpec 1834 expectSubnet infrav1.SubnetSpec 1835 }{ 1836 { 1837 clusterName: "my-cluster-1", 1838 subnetName: "subnet-1", 1839 azureClusterNetworkSpec: infrav1.NetworkSpec{}, 1840 expectSubnet: infrav1.SubnetSpec{}, 1841 }, 1842 { 1843 clusterName: "my-cluster-1", 1844 subnetName: "subnet-1", 1845 azureClusterNetworkSpec: infrav1.NetworkSpec{ 1846 Subnets: infrav1.Subnets{ 1847 infrav1.SubnetSpec{ 1848 SubnetClassSpec: infrav1.SubnetClassSpec{ 1849 Name: "subnet-1", 1850 }, 1851 ID: "subnet-1-id", 1852 }, 1853 infrav1.SubnetSpec{ 1854 SubnetClassSpec: infrav1.SubnetClassSpec{ 1855 Name: "subnet-2", 1856 }, 1857 ID: "subnet-1-id", 1858 }, 1859 infrav1.SubnetSpec{ 1860 SubnetClassSpec: infrav1.SubnetClassSpec{ 1861 Name: "subnet-3", 1862 }, 1863 ID: "subnet-2-id", 1864 }, 1865 }, 1866 }, 1867 expectSubnet: infrav1.SubnetSpec{ 1868 SubnetClassSpec: infrav1.SubnetClassSpec{ 1869 Name: "subnet-1", 1870 }, 1871 ID: "subnet-1-id", 1872 }, 1873 }, 1874 } 1875 for _, tc := range tests { 1876 t.Run(tc.clusterName, func(t *testing.T) { 1877 g := NewWithT(t) 1878 scheme := runtime.NewScheme() 1879 _ = infrav1.AddToScheme(scheme) 1880 _ = clusterv1.AddToScheme(scheme) 1881 _ = corev1.AddToScheme(scheme) 1882 1883 cluster := &clusterv1.Cluster{ 1884 ObjectMeta: metav1.ObjectMeta{ 1885 Name: tc.clusterName, 1886 Namespace: "default", 1887 }, 1888 } 1889 azureCluster := &infrav1.AzureCluster{ 1890 ObjectMeta: metav1.ObjectMeta{ 1891 Name: tc.clusterName, 1892 OwnerReferences: []metav1.OwnerReference{ 1893 { 1894 APIVersion: "cluster.x-k8s.io/v1beta1", 1895 Kind: "Cluster", 1896 Name: "my-cluster", 1897 }, 1898 }, 1899 }, 1900 Spec: infrav1.AzureClusterSpec{ 1901 NetworkSpec: tc.azureClusterNetworkSpec, 1902 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 1903 SubscriptionID: "123", 1904 IdentityRef: &corev1.ObjectReference{ 1905 Kind: infrav1.AzureClusterIdentityKind, 1906 }, 1907 }, 1908 }, 1909 } 1910 fakeIdentity := &infrav1.AzureClusterIdentity{ 1911 Spec: infrav1.AzureClusterIdentitySpec{ 1912 Type: infrav1.ServicePrincipal, 1913 ClientID: fakeClientID, 1914 TenantID: fakeTenantID, 1915 }, 1916 } 1917 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 1918 1919 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 1920 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 1921 1922 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 1923 Cluster: cluster, 1924 AzureCluster: azureCluster, 1925 Client: fakeClient, 1926 }) 1927 g.Expect(err).NotTo(HaveOccurred()) 1928 got := clusterScope.Subnet(tc.subnetName) 1929 g.Expect(tc.expectSubnet).Should(Equal(got)) 1930 }) 1931 } 1932 } 1933 1934 func TestControlPlaneRouteTable(t *testing.T) { 1935 tests := []struct { 1936 clusterName string 1937 azureClusterNetworkSpec infrav1.NetworkSpec 1938 expectRouteTable infrav1.RouteTable 1939 }{ 1940 { 1941 clusterName: "my-cluster-1", 1942 azureClusterNetworkSpec: infrav1.NetworkSpec{}, 1943 expectRouteTable: infrav1.RouteTable{}, 1944 }, 1945 { 1946 clusterName: "my-cluster-2", 1947 azureClusterNetworkSpec: infrav1.NetworkSpec{ 1948 Subnets: infrav1.Subnets{ 1949 infrav1.SubnetSpec{ 1950 RouteTable: infrav1.RouteTable{ 1951 ID: "fake-id-1", 1952 Name: "route-tb-1", 1953 }, 1954 SubnetClassSpec: infrav1.SubnetClassSpec{ 1955 Role: infrav1.SubnetNode, 1956 }, 1957 }, 1958 infrav1.SubnetSpec{ 1959 RouteTable: infrav1.RouteTable{ 1960 ID: "fake-id-2", 1961 Name: "route-tb-2", 1962 }, 1963 SubnetClassSpec: infrav1.SubnetClassSpec{ 1964 Role: infrav1.SubnetControlPlane, 1965 }, 1966 }, 1967 infrav1.SubnetSpec{ 1968 RouteTable: infrav1.RouteTable{ 1969 ID: "fake-id-3", 1970 Name: "route-tb-3", 1971 }, 1972 SubnetClassSpec: infrav1.SubnetClassSpec{ 1973 Role: infrav1.SubnetBastion, 1974 }, 1975 }, 1976 }, 1977 }, 1978 expectRouteTable: infrav1.RouteTable{ 1979 ID: "fake-id-2", 1980 Name: "route-tb-2", 1981 }, 1982 }, 1983 } 1984 for _, tc := range tests { 1985 t.Run(tc.clusterName, func(t *testing.T) { 1986 g := NewWithT(t) 1987 scheme := runtime.NewScheme() 1988 _ = infrav1.AddToScheme(scheme) 1989 _ = clusterv1.AddToScheme(scheme) 1990 _ = corev1.AddToScheme(scheme) 1991 1992 cluster := &clusterv1.Cluster{ 1993 ObjectMeta: metav1.ObjectMeta{ 1994 Name: tc.clusterName, 1995 Namespace: "default", 1996 }, 1997 } 1998 azureCluster := &infrav1.AzureCluster{ 1999 ObjectMeta: metav1.ObjectMeta{ 2000 Name: tc.clusterName, 2001 OwnerReferences: []metav1.OwnerReference{ 2002 { 2003 APIVersion: "cluster.x-k8s.io/v1beta1", 2004 Kind: "Cluster", 2005 Name: "my-cluster", 2006 }, 2007 }, 2008 }, 2009 Spec: infrav1.AzureClusterSpec{ 2010 NetworkSpec: tc.azureClusterNetworkSpec, 2011 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2012 SubscriptionID: "123", 2013 IdentityRef: &corev1.ObjectReference{ 2014 Kind: infrav1.AzureClusterIdentityKind, 2015 }, 2016 }, 2017 }, 2018 } 2019 fakeIdentity := &infrav1.AzureClusterIdentity{ 2020 Spec: infrav1.AzureClusterIdentitySpec{ 2021 Type: infrav1.ServicePrincipal, 2022 ClientID: fakeClientID, 2023 TenantID: fakeTenantID, 2024 }, 2025 } 2026 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2027 2028 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2029 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2030 2031 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2032 Cluster: cluster, 2033 AzureCluster: azureCluster, 2034 Client: fakeClient, 2035 }) 2036 g.Expect(err).NotTo(HaveOccurred()) 2037 got := clusterScope.ControlPlaneRouteTable() 2038 g.Expect(tc.expectRouteTable).Should(Equal(got)) 2039 }) 2040 } 2041 } 2042 2043 func TestGetPrivateDNSZoneName(t *testing.T) { 2044 tests := []struct { 2045 clusterName string 2046 azureClusterNetworkSpec infrav1.NetworkSpec 2047 expectPrivateDNSZoneName string 2048 }{ 2049 { 2050 clusterName: "my-cluster-1", 2051 azureClusterNetworkSpec: infrav1.NetworkSpec{ 2052 NetworkClassSpec: infrav1.NetworkClassSpec{ 2053 PrivateDNSZoneName: "fake-privateDNSZoneName", 2054 }, 2055 }, 2056 expectPrivateDNSZoneName: "fake-privateDNSZoneName", 2057 }, 2058 { 2059 clusterName: "my-cluster-2", 2060 expectPrivateDNSZoneName: "my-cluster-2.capz.io", 2061 }, 2062 } 2063 for _, tc := range tests { 2064 t.Run(tc.clusterName, func(t *testing.T) { 2065 g := NewWithT(t) 2066 scheme := runtime.NewScheme() 2067 _ = infrav1.AddToScheme(scheme) 2068 _ = clusterv1.AddToScheme(scheme) 2069 _ = corev1.AddToScheme(scheme) 2070 2071 cluster := &clusterv1.Cluster{ 2072 ObjectMeta: metav1.ObjectMeta{ 2073 Name: tc.clusterName, 2074 Namespace: "default", 2075 }, 2076 } 2077 azureCluster := &infrav1.AzureCluster{ 2078 ObjectMeta: metav1.ObjectMeta{ 2079 Name: tc.clusterName, 2080 OwnerReferences: []metav1.OwnerReference{ 2081 { 2082 APIVersion: "cluster.x-k8s.io/v1beta1", 2083 Kind: "Cluster", 2084 Name: "my-cluster", 2085 }, 2086 }, 2087 }, 2088 Spec: infrav1.AzureClusterSpec{ 2089 NetworkSpec: tc.azureClusterNetworkSpec, 2090 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2091 SubscriptionID: "123", 2092 IdentityRef: &corev1.ObjectReference{ 2093 Kind: infrav1.AzureClusterIdentityKind, 2094 }, 2095 }, 2096 }, 2097 } 2098 fakeIdentity := &infrav1.AzureClusterIdentity{ 2099 Spec: infrav1.AzureClusterIdentitySpec{ 2100 Type: infrav1.ServicePrincipal, 2101 ClientID: fakeClientID, 2102 TenantID: fakeTenantID, 2103 }, 2104 } 2105 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2106 2107 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2108 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2109 2110 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2111 Cluster: cluster, 2112 AzureCluster: azureCluster, 2113 Client: fakeClient, 2114 }) 2115 g.Expect(err).NotTo(HaveOccurred()) 2116 got := clusterScope.GetPrivateDNSZoneName() 2117 g.Expect(tc.expectPrivateDNSZoneName).Should(Equal(got)) 2118 }) 2119 } 2120 } 2121 2122 func TestAPIServerLBPoolName(t *testing.T) { 2123 tests := []struct { 2124 lbName string 2125 clusterName string 2126 expectLBpoolName string 2127 }{ 2128 { 2129 lbName: "fake-lb-1", 2130 clusterName: "my-cluster-1", 2131 expectLBpoolName: "fake-lb-1-backendPool", 2132 }, 2133 { 2134 lbName: "fake-lb-2", 2135 clusterName: "my-cluster-2", 2136 expectLBpoolName: "fake-lb-2-backendPool", 2137 }, 2138 } 2139 for _, tc := range tests { 2140 t.Run(tc.lbName, func(t *testing.T) { 2141 g := NewWithT(t) 2142 scheme := runtime.NewScheme() 2143 _ = infrav1.AddToScheme(scheme) 2144 _ = clusterv1.AddToScheme(scheme) 2145 _ = corev1.AddToScheme(scheme) 2146 2147 cluster := &clusterv1.Cluster{ 2148 ObjectMeta: metav1.ObjectMeta{ 2149 Name: tc.clusterName, 2150 Namespace: "default", 2151 }, 2152 } 2153 azureCluster := &infrav1.AzureCluster{ 2154 ObjectMeta: metav1.ObjectMeta{ 2155 Name: tc.clusterName, 2156 OwnerReferences: []metav1.OwnerReference{ 2157 { 2158 APIVersion: "cluster.x-k8s.io/v1beta1", 2159 Kind: "Cluster", 2160 Name: "my-cluster", 2161 }, 2162 }, 2163 }, 2164 Spec: infrav1.AzureClusterSpec{ 2165 NetworkSpec: infrav1.NetworkSpec{ 2166 APIServerLB: infrav1.LoadBalancerSpec{ 2167 Name: tc.lbName, 2168 }, 2169 }, 2170 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2171 SubscriptionID: "123", 2172 IdentityRef: &corev1.ObjectReference{ 2173 Kind: infrav1.AzureClusterIdentityKind, 2174 }, 2175 }, 2176 }, 2177 } 2178 fakeIdentity := &infrav1.AzureClusterIdentity{ 2179 Spec: infrav1.AzureClusterIdentitySpec{ 2180 Type: infrav1.ServicePrincipal, 2181 ClientID: fakeClientID, 2182 TenantID: fakeTenantID, 2183 }, 2184 } 2185 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2186 2187 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2188 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2189 2190 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2191 Cluster: cluster, 2192 AzureCluster: azureCluster, 2193 Client: fakeClient, 2194 }) 2195 clusterScope.AzureCluster.SetBackendPoolNameDefault() 2196 g.Expect(err).NotTo(HaveOccurred()) 2197 got := clusterScope.APIServerLBPoolName() 2198 g.Expect(tc.expectLBpoolName).Should(Equal(got)) 2199 }) 2200 } 2201 } 2202 2203 func TestOutboundLBName(t *testing.T) { 2204 tests := []struct { 2205 clusterName string 2206 name string 2207 role string 2208 apiServerLB *infrav1.LoadBalancerSpec 2209 controlPlaneOutboundLB *infrav1.LoadBalancerSpec 2210 nodeOutboundLB *infrav1.LoadBalancerSpec 2211 expected string 2212 }{ 2213 { 2214 clusterName: "my-cluster", 2215 name: "public cluster node outbound lb", 2216 role: "node", 2217 expected: "", 2218 }, 2219 { 2220 clusterName: "my-cluster", 2221 name: "public cluster control plane outbound lb", 2222 role: "control-plane", 2223 expected: "my-cluster-public-lb", 2224 }, 2225 { 2226 clusterName: "my-cluster", 2227 name: "private cluster with node outbound lb", 2228 role: "node", 2229 nodeOutboundLB: &infrav1.LoadBalancerSpec{}, 2230 apiServerLB: &infrav1.LoadBalancerSpec{ 2231 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 2232 Type: "Internal", 2233 }}, 2234 expected: "my-cluster", 2235 }, 2236 { 2237 clusterName: "my-cluster", 2238 name: "private cluster without node outbound lb", 2239 role: "node", 2240 apiServerLB: &infrav1.LoadBalancerSpec{ 2241 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 2242 Type: "Internal", 2243 }}, 2244 expected: "", 2245 }, 2246 { 2247 clusterName: "my-cluster", 2248 name: "private cluster with control plane outbound lb", 2249 role: "control-plane", 2250 controlPlaneOutboundLB: &infrav1.LoadBalancerSpec{Name: "cp-outbound-lb"}, 2251 apiServerLB: &infrav1.LoadBalancerSpec{ 2252 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 2253 Type: "Internal", 2254 }}, 2255 expected: "cp-outbound-lb", 2256 }, 2257 { 2258 clusterName: "my-cluster", 2259 name: "private cluster without control plane outbound lb", 2260 role: "control-plane", 2261 apiServerLB: &infrav1.LoadBalancerSpec{ 2262 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 2263 Type: "Internal", 2264 }}, 2265 expected: "", 2266 }, 2267 } 2268 2269 for _, tc := range tests { 2270 t.Run(tc.name, func(t *testing.T) { 2271 g := NewWithT(t) 2272 scheme := runtime.NewScheme() 2273 _ = infrav1.AddToScheme(scheme) 2274 _ = clusterv1.AddToScheme(scheme) 2275 _ = corev1.AddToScheme(scheme) 2276 2277 cluster := &clusterv1.Cluster{ 2278 ObjectMeta: metav1.ObjectMeta{ 2279 Name: tc.clusterName, 2280 Namespace: "default", 2281 }, 2282 } 2283 2284 azureCluster := &infrav1.AzureCluster{ 2285 ObjectMeta: metav1.ObjectMeta{ 2286 Name: tc.clusterName, 2287 OwnerReferences: []metav1.OwnerReference{ 2288 { 2289 APIVersion: "cluster.x-k8s.io/v1beta1", 2290 Kind: "Cluster", 2291 Name: "my-cluster", 2292 }, 2293 }, 2294 }, 2295 Spec: infrav1.AzureClusterSpec{ 2296 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2297 SubscriptionID: "123", 2298 IdentityRef: &corev1.ObjectReference{ 2299 Kind: infrav1.AzureClusterIdentityKind, 2300 }, 2301 }, 2302 NetworkSpec: infrav1.NetworkSpec{ 2303 Subnets: infrav1.Subnets{ 2304 { 2305 SubnetClassSpec: infrav1.SubnetClassSpec{ 2306 Name: "node", 2307 Role: infrav1.SubnetNode, 2308 }, 2309 }, 2310 }, 2311 }, 2312 }, 2313 } 2314 2315 if tc.apiServerLB != nil { 2316 azureCluster.Spec.NetworkSpec.APIServerLB = *tc.apiServerLB 2317 } 2318 2319 if tc.controlPlaneOutboundLB != nil { 2320 azureCluster.Spec.NetworkSpec.ControlPlaneOutboundLB = tc.controlPlaneOutboundLB 2321 } 2322 2323 if tc.nodeOutboundLB != nil { 2324 azureCluster.Spec.NetworkSpec.NodeOutboundLB = tc.nodeOutboundLB 2325 } 2326 2327 azureCluster.Default() 2328 2329 fakeIdentity := &infrav1.AzureClusterIdentity{ 2330 Spec: infrav1.AzureClusterIdentitySpec{ 2331 Type: infrav1.ServicePrincipal, 2332 ClientID: fakeClientID, 2333 TenantID: fakeTenantID, 2334 }, 2335 } 2336 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2337 2338 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2339 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2340 2341 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2342 Cluster: cluster, 2343 AzureCluster: azureCluster, 2344 Client: fakeClient, 2345 }) 2346 clusterScope.AzureCluster.SetBackendPoolNameDefault() 2347 g.Expect(err).NotTo(HaveOccurred()) 2348 got := clusterScope.OutboundLBName(tc.role) 2349 g.Expect(tc.expected).Should(Equal(got)) 2350 }) 2351 } 2352 } 2353 2354 func TestBackendPoolName(t *testing.T) { 2355 tests := []struct { 2356 name string 2357 clusterName string 2358 2359 customAPIServerBackendPoolName string 2360 customNodeBackendPoolName string 2361 customControlPlaneBackendPoolName string 2362 2363 expectedAPIServerBackendPoolName string 2364 expectedNodeBackendPoolName string 2365 expectedControlPlaneBackendPoolName string 2366 }{ 2367 { 2368 name: "With default backend pool names", 2369 clusterName: "my-cluster", 2370 expectedAPIServerBackendPoolName: "APIServerLBName-backendPool", 2371 expectedNodeBackendPoolName: "NodeOutboundLBName-outboundBackendPool", 2372 expectedControlPlaneBackendPoolName: "my-cluster-outbound-lb-outboundBackendPool", 2373 }, 2374 { 2375 name: "With custom node backend pool name", 2376 clusterName: "my-cluster", 2377 2378 // setting custom name for node pool name only, others should stay the same 2379 customNodeBackendPoolName: "custom-node-poolname", 2380 2381 expectedAPIServerBackendPoolName: "APIServerLBName-backendPool", 2382 expectedNodeBackendPoolName: "custom-node-poolname", 2383 expectedControlPlaneBackendPoolName: "my-cluster-outbound-lb-outboundBackendPool", 2384 }, 2385 { 2386 name: "With custom backends pool name", 2387 clusterName: "my-cluster", 2388 2389 // setting custom names for all backends pools 2390 customAPIServerBackendPoolName: "custom-api-server-poolname", 2391 customNodeBackendPoolName: "custom-node-poolname", 2392 customControlPlaneBackendPoolName: "custom-control-plane-poolname", 2393 2394 expectedAPIServerBackendPoolName: "custom-api-server-poolname", 2395 expectedNodeBackendPoolName: "custom-node-poolname", 2396 expectedControlPlaneBackendPoolName: "custom-control-plane-poolname", 2397 }, 2398 } 2399 for _, tc := range tests { 2400 t.Run(tc.name, func(t *testing.T) { 2401 g := NewWithT(t) 2402 scheme := runtime.NewScheme() 2403 _ = infrav1.AddToScheme(scheme) 2404 _ = clusterv1.AddToScheme(scheme) 2405 _ = corev1.AddToScheme(scheme) 2406 2407 cluster := &clusterv1.Cluster{ 2408 ObjectMeta: metav1.ObjectMeta{ 2409 Name: tc.clusterName, 2410 Namespace: "default", 2411 }, 2412 } 2413 2414 azureCluster := &infrav1.AzureCluster{ 2415 ObjectMeta: metav1.ObjectMeta{ 2416 Name: tc.clusterName, 2417 OwnerReferences: []metav1.OwnerReference{ 2418 { 2419 APIVersion: "cluster.x-k8s.io/v1beta1", 2420 Kind: "Cluster", 2421 Name: tc.clusterName, 2422 }, 2423 }, 2424 }, 2425 Spec: infrav1.AzureClusterSpec{ 2426 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2427 SubscriptionID: "123", 2428 IdentityRef: &corev1.ObjectReference{ 2429 Kind: infrav1.AzureClusterIdentityKind, 2430 }, 2431 }, 2432 NetworkSpec: infrav1.NetworkSpec{ 2433 Subnets: infrav1.Subnets{ 2434 { 2435 SubnetClassSpec: infrav1.SubnetClassSpec{ 2436 Role: infrav1.SubnetNode, 2437 Name: "node", 2438 }, 2439 }, 2440 }, 2441 APIServerLB: infrav1.LoadBalancerSpec{ 2442 Name: "APIServerLBName", 2443 }, 2444 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 2445 Name: "ControlPlaneOutboundLBName", 2446 }, 2447 NodeOutboundLB: &infrav1.LoadBalancerSpec{ 2448 Name: "NodeOutboundLBName", 2449 }, 2450 }, 2451 }, 2452 } 2453 2454 azureCluster.Default() 2455 2456 fakeIdentity := &infrav1.AzureClusterIdentity{ 2457 Spec: infrav1.AzureClusterIdentitySpec{ 2458 Type: infrav1.ServicePrincipal, 2459 ClientID: fakeClientID, 2460 TenantID: fakeTenantID, 2461 }, 2462 } 2463 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2464 2465 if tc.customAPIServerBackendPoolName != "" { 2466 azureCluster.Spec.NetworkSpec.APIServerLB.BackendPool.Name = tc.customAPIServerBackendPoolName 2467 } 2468 2469 if tc.customNodeBackendPoolName != "" { 2470 azureCluster.Spec.NetworkSpec.NodeOutboundLB.BackendPool.Name = tc.customNodeBackendPoolName 2471 } 2472 2473 if tc.customControlPlaneBackendPoolName != "" { 2474 azureCluster.Spec.NetworkSpec.ControlPlaneOutboundLB.BackendPool.Name = tc.customControlPlaneBackendPoolName 2475 } 2476 2477 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2478 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2479 2480 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2481 Cluster: cluster, 2482 AzureCluster: azureCluster, 2483 Client: fakeClient, 2484 }) 2485 clusterScope.AzureCluster.SetBackendPoolNameDefault() 2486 g.Expect(err).NotTo(HaveOccurred()) 2487 got := clusterScope.LBSpecs() 2488 g.Expect(got).To(HaveLen(3)) 2489 2490 // API server backend pool name 2491 apiServerLBSpec := got[0].(*loadbalancers.LBSpec) 2492 g.Expect(apiServerLBSpec.BackendPoolName).To(Equal(tc.expectedAPIServerBackendPoolName)) 2493 g.Expect(apiServerLBSpec.Role).To(Equal(infrav1.APIServerRole)) 2494 2495 // Node backend pool name 2496 NodeLBSpec := got[1].(*loadbalancers.LBSpec) 2497 g.Expect(NodeLBSpec.BackendPoolName).To(Equal(tc.expectedNodeBackendPoolName)) 2498 g.Expect(NodeLBSpec.Role).To(Equal(infrav1.NodeOutboundRole)) 2499 2500 // Control Plane backend pool name 2501 controlPlaneLBSpec := got[2].(*loadbalancers.LBSpec) 2502 g.Expect(controlPlaneLBSpec.BackendPoolName).To(Equal(tc.expectedControlPlaneBackendPoolName)) 2503 g.Expect(controlPlaneLBSpec.Role).To(Equal(infrav1.ControlPlaneOutboundRole)) 2504 }) 2505 } 2506 } 2507 2508 func TestOutboundPoolName(t *testing.T) { 2509 tests := []struct { 2510 name string 2511 clusterName string 2512 loadBalancerName string 2513 expectOutboundPoolName string 2514 }{ 2515 { 2516 name: "Empty loadBalancerName", 2517 clusterName: "my-cluster", 2518 loadBalancerName: "", 2519 expectOutboundPoolName: "", 2520 }, 2521 { 2522 name: "Non empty loadBalancerName", 2523 clusterName: "my-cluster", 2524 loadBalancerName: "my-loadbalancer", 2525 expectOutboundPoolName: "my-loadbalancer-outboundBackendPool", 2526 }, 2527 } 2528 for _, tc := range tests { 2529 t.Run(tc.name, func(t *testing.T) { 2530 g := NewWithT(t) 2531 scheme := runtime.NewScheme() 2532 _ = infrav1.AddToScheme(scheme) 2533 _ = clusterv1.AddToScheme(scheme) 2534 _ = corev1.AddToScheme(scheme) 2535 2536 cluster := &clusterv1.Cluster{ 2537 ObjectMeta: metav1.ObjectMeta{ 2538 Name: tc.clusterName, 2539 Namespace: "default", 2540 }, 2541 } 2542 azureCluster := &infrav1.AzureCluster{ 2543 ObjectMeta: metav1.ObjectMeta{ 2544 Name: tc.clusterName, 2545 OwnerReferences: []metav1.OwnerReference{ 2546 { 2547 APIVersion: "cluster.x-k8s.io/v1beta1", 2548 Kind: "Cluster", 2549 Name: "my-cluster", 2550 }, 2551 }, 2552 }, 2553 Spec: infrav1.AzureClusterSpec{ 2554 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2555 SubscriptionID: "123", 2556 IdentityRef: &corev1.ObjectReference{ 2557 Kind: infrav1.AzureClusterIdentityKind, 2558 }, 2559 }, 2560 }, 2561 } 2562 fakeIdentity := &infrav1.AzureClusterIdentity{ 2563 Spec: infrav1.AzureClusterIdentitySpec{ 2564 Type: infrav1.ServicePrincipal, 2565 ClientID: fakeClientID, 2566 TenantID: fakeTenantID, 2567 }, 2568 } 2569 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2570 2571 if tc.loadBalancerName != "" { 2572 azureCluster.Spec.NetworkSpec.NodeOutboundLB = &infrav1.LoadBalancerSpec{ 2573 Name: tc.loadBalancerName, 2574 } 2575 } 2576 2577 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2578 azureCluster.Default() 2579 2580 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2581 2582 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2583 Cluster: cluster, 2584 AzureCluster: azureCluster, 2585 Client: fakeClient, 2586 }) 2587 clusterScope.AzureCluster.SetBackendPoolNameDefault() 2588 g.Expect(err).NotTo(HaveOccurred()) 2589 got := clusterScope.OutboundPoolName(infrav1.Node) 2590 g.Expect(tc.expectOutboundPoolName).Should(Equal(got)) 2591 }) 2592 } 2593 } 2594 2595 func TestGenerateFQDN(t *testing.T) { 2596 tests := []struct { 2597 clusterName string 2598 ipName string 2599 subscriptionID string 2600 resourceGroup string 2601 location string 2602 expectFQDN string 2603 }{ 2604 { 2605 clusterName: "my-cluster", 2606 ipName: "172.123.45.78", 2607 subscriptionID: "123", 2608 resourceGroup: "my-rg", 2609 location: "eastus", 2610 }, 2611 { 2612 clusterName: "my-cluster-1", 2613 ipName: "172.123.45.79", 2614 subscriptionID: "567", 2615 resourceGroup: "my-rg-1", 2616 location: "westus", 2617 }, 2618 { 2619 clusterName: "my-cluster-2", 2620 ipName: "172.123.45.80", 2621 subscriptionID: "183", 2622 resourceGroup: "my-rg-2", 2623 location: "centralasia", 2624 }, 2625 } 2626 for _, tc := range tests { 2627 t.Run(tc.clusterName, func(t *testing.T) { 2628 g := NewWithT(t) 2629 scheme := runtime.NewScheme() 2630 _ = infrav1.AddToScheme(scheme) 2631 _ = clusterv1.AddToScheme(scheme) 2632 _ = corev1.AddToScheme(scheme) 2633 2634 cluster := &clusterv1.Cluster{ 2635 ObjectMeta: metav1.ObjectMeta{ 2636 Name: tc.clusterName, 2637 Namespace: "default", 2638 }, 2639 } 2640 azureCluster := &infrav1.AzureCluster{ 2641 ObjectMeta: metav1.ObjectMeta{ 2642 Name: tc.clusterName, 2643 OwnerReferences: []metav1.OwnerReference{ 2644 { 2645 APIVersion: "cluster.x-k8s.io/v1beta1", 2646 Kind: "Cluster", 2647 Name: "my-cluster", 2648 }, 2649 }, 2650 }, 2651 Spec: infrav1.AzureClusterSpec{ 2652 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2653 SubscriptionID: "123", 2654 Location: tc.location, 2655 IdentityRef: &corev1.ObjectReference{ 2656 Kind: infrav1.AzureClusterIdentityKind, 2657 }, 2658 }, 2659 ResourceGroup: tc.resourceGroup, 2660 }, 2661 } 2662 fakeIdentity := &infrav1.AzureClusterIdentity{ 2663 Spec: infrav1.AzureClusterIdentitySpec{ 2664 Type: infrav1.ServicePrincipal, 2665 ClientID: fakeClientID, 2666 TenantID: fakeTenantID, 2667 }, 2668 } 2669 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2670 2671 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2672 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2673 2674 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2675 Cluster: cluster, 2676 AzureCluster: azureCluster, 2677 Client: fakeClient, 2678 }) 2679 g.Expect(err).NotTo(HaveOccurred()) 2680 got := clusterScope.GenerateFQDN(tc.ipName) 2681 g.Expect(got).Should(ContainSubstring(tc.clusterName)) 2682 g.Expect(got).Should(ContainSubstring(tc.location)) 2683 }) 2684 } 2685 } 2686 2687 func TestAdditionalTags(t *testing.T) { 2688 tests := []struct { 2689 name string 2690 clusterName string 2691 azureClusterAdditionalTags infrav1.Tags 2692 expectTags infrav1.Tags 2693 }{ 2694 { 2695 name: "Nil tags", 2696 clusterName: "my-cluster", 2697 expectTags: infrav1.Tags{}, 2698 }, 2699 2700 { 2701 name: "Single tag present in azure cluster spec", 2702 clusterName: "my-cluster", 2703 azureClusterAdditionalTags: infrav1.Tags{ 2704 "fake-id-1": "fake-value-1", 2705 }, 2706 expectTags: infrav1.Tags{ 2707 "fake-id-1": "fake-value-1", 2708 }, 2709 }, 2710 { 2711 name: "Multiple tags present in azure cluster spec", 2712 clusterName: "my-cluster", 2713 azureClusterAdditionalTags: infrav1.Tags{ 2714 "fake-id-1": "fake-value-1", 2715 "fake-id-2": "fake-value-2", 2716 "fake-id-3": "fake-value-3", 2717 }, 2718 expectTags: infrav1.Tags{ 2719 "fake-id-1": "fake-value-1", 2720 "fake-id-2": "fake-value-2", 2721 "fake-id-3": "fake-value-3", 2722 }, 2723 }, 2724 } 2725 for _, tc := range tests { 2726 t.Run(tc.name, func(t *testing.T) { 2727 g := NewWithT(t) 2728 scheme := runtime.NewScheme() 2729 _ = infrav1.AddToScheme(scheme) 2730 _ = clusterv1.AddToScheme(scheme) 2731 _ = corev1.AddToScheme(scheme) 2732 2733 cluster := &clusterv1.Cluster{ 2734 ObjectMeta: metav1.ObjectMeta{ 2735 Name: tc.clusterName, 2736 Namespace: "default", 2737 }, 2738 } 2739 azureCluster := &infrav1.AzureCluster{ 2740 ObjectMeta: metav1.ObjectMeta{ 2741 Name: tc.clusterName, 2742 OwnerReferences: []metav1.OwnerReference{ 2743 { 2744 APIVersion: "cluster.x-k8s.io/v1beta1", 2745 Kind: "Cluster", 2746 Name: "my-cluster", 2747 }, 2748 }, 2749 }, 2750 Spec: infrav1.AzureClusterSpec{ 2751 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2752 SubscriptionID: "123", 2753 AdditionalTags: tc.azureClusterAdditionalTags, 2754 IdentityRef: &corev1.ObjectReference{ 2755 Kind: infrav1.AzureClusterIdentityKind, 2756 }, 2757 }, 2758 }, 2759 } 2760 fakeIdentity := &infrav1.AzureClusterIdentity{ 2761 Spec: infrav1.AzureClusterIdentitySpec{ 2762 Type: infrav1.ServicePrincipal, 2763 ClientID: fakeClientID, 2764 TenantID: fakeTenantID, 2765 }, 2766 } 2767 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2768 2769 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2770 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2771 2772 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2773 Cluster: cluster, 2774 AzureCluster: azureCluster, 2775 Client: fakeClient, 2776 }) 2777 g.Expect(err).NotTo(HaveOccurred()) 2778 got := clusterScope.AdditionalTags() 2779 g.Expect(tc.expectTags).Should(Equal(got)) 2780 }) 2781 } 2782 } 2783 2784 func TestAPIServerPort(t *testing.T) { 2785 tests := []struct { 2786 name string 2787 clusterName string 2788 clusterNetowrk *clusterv1.ClusterNetwork 2789 expectAPIServerPort int32 2790 }{ 2791 { 2792 name: "Nil cluster network", 2793 clusterName: "my-cluster", 2794 expectAPIServerPort: 6443, 2795 }, 2796 2797 { 2798 name: "Non nil cluster network but nil apiserverport", 2799 clusterName: "my-cluster", 2800 clusterNetowrk: &clusterv1.ClusterNetwork{}, 2801 expectAPIServerPort: 6443, 2802 }, 2803 { 2804 name: "Non nil cluster network and non nil apiserverport", 2805 clusterName: "my-cluster", 2806 clusterNetowrk: &clusterv1.ClusterNetwork{ 2807 APIServerPort: ptr.To[int32](7000), 2808 }, 2809 expectAPIServerPort: 7000, 2810 }, 2811 } 2812 for _, tc := range tests { 2813 t.Run(tc.name, func(t *testing.T) { 2814 g := NewWithT(t) 2815 scheme := runtime.NewScheme() 2816 _ = infrav1.AddToScheme(scheme) 2817 _ = clusterv1.AddToScheme(scheme) 2818 _ = corev1.AddToScheme(scheme) 2819 2820 cluster := &clusterv1.Cluster{ 2821 ObjectMeta: metav1.ObjectMeta{ 2822 Name: tc.clusterName, 2823 Namespace: "default", 2824 }, 2825 Spec: clusterv1.ClusterSpec{ 2826 ClusterNetwork: tc.clusterNetowrk, 2827 }, 2828 } 2829 azureCluster := &infrav1.AzureCluster{ 2830 ObjectMeta: metav1.ObjectMeta{ 2831 Name: tc.clusterName, 2832 OwnerReferences: []metav1.OwnerReference{ 2833 { 2834 APIVersion: "cluster.x-k8s.io/v1beta1", 2835 Kind: "Cluster", 2836 Name: "my-cluster", 2837 }, 2838 }, 2839 }, 2840 Spec: infrav1.AzureClusterSpec{ 2841 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2842 SubscriptionID: "123", 2843 IdentityRef: &corev1.ObjectReference{ 2844 Kind: infrav1.AzureClusterIdentityKind, 2845 }, 2846 }, 2847 }, 2848 } 2849 fakeIdentity := &infrav1.AzureClusterIdentity{ 2850 Spec: infrav1.AzureClusterIdentitySpec{ 2851 Type: infrav1.ServicePrincipal, 2852 ClientID: fakeClientID, 2853 TenantID: fakeTenantID, 2854 }, 2855 } 2856 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2857 2858 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2859 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2860 2861 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2862 Cluster: cluster, 2863 AzureCluster: azureCluster, 2864 Client: fakeClient, 2865 }) 2866 g.Expect(err).NotTo(HaveOccurred()) 2867 got := clusterScope.APIServerPort() 2868 g.Expect(tc.expectAPIServerPort).Should(Equal(got)) 2869 }) 2870 } 2871 } 2872 2873 func TestFailureDomains(t *testing.T) { 2874 tests := []struct { 2875 name string 2876 expectFailureDomains []*string 2877 clusterName string 2878 azureClusterStatus infrav1.AzureClusterStatus 2879 }{ 2880 { 2881 name: "Empty azure cluster status", 2882 expectFailureDomains: []*string{}, 2883 clusterName: "my-cluster", 2884 }, 2885 2886 { 2887 name: "Single failure domain present in azure cluster status", 2888 expectFailureDomains: []*string{ptr.To("failure-domain-id")}, 2889 clusterName: "my-cluster", 2890 azureClusterStatus: infrav1.AzureClusterStatus{ 2891 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 2892 "failure-domain-id": {}, 2893 }, 2894 }, 2895 }, 2896 { 2897 name: "Multiple failure domains present in azure cluster status", 2898 expectFailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")}, 2899 clusterName: "my-cluster", 2900 azureClusterStatus: infrav1.AzureClusterStatus{ 2901 FailureDomains: map[string]clusterv1.FailureDomainSpec{ 2902 "failure-domain-id-1": {}, 2903 "failure-domain-id-2": {}, 2904 "failure-domain-id-3": {}, 2905 }, 2906 }, 2907 }, 2908 } 2909 for _, tc := range tests { 2910 t.Run(tc.name, func(t *testing.T) { 2911 g := NewWithT(t) 2912 scheme := runtime.NewScheme() 2913 _ = infrav1.AddToScheme(scheme) 2914 _ = clusterv1.AddToScheme(scheme) 2915 _ = corev1.AddToScheme(scheme) 2916 2917 cluster := &clusterv1.Cluster{ 2918 ObjectMeta: metav1.ObjectMeta{ 2919 Name: tc.clusterName, 2920 Namespace: "default", 2921 }, 2922 } 2923 azureCluster := &infrav1.AzureCluster{ 2924 ObjectMeta: metav1.ObjectMeta{ 2925 Name: tc.clusterName, 2926 OwnerReferences: []metav1.OwnerReference{ 2927 { 2928 APIVersion: "cluster.x-k8s.io/v1beta1", 2929 Kind: "Cluster", 2930 Name: "my-cluster", 2931 }, 2932 }, 2933 }, 2934 Spec: infrav1.AzureClusterSpec{ 2935 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2936 SubscriptionID: "123", 2937 IdentityRef: &corev1.ObjectReference{ 2938 Kind: infrav1.AzureClusterIdentityKind, 2939 }, 2940 }, 2941 }, 2942 Status: tc.azureClusterStatus, 2943 } 2944 fakeIdentity := &infrav1.AzureClusterIdentity{ 2945 Spec: infrav1.AzureClusterIdentitySpec{ 2946 Type: infrav1.ServicePrincipal, 2947 ClientID: fakeClientID, 2948 TenantID: fakeTenantID, 2949 }, 2950 } 2951 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 2952 2953 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 2954 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 2955 2956 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 2957 Cluster: cluster, 2958 AzureCluster: azureCluster, 2959 Client: fakeClient, 2960 }) 2961 g.Expect(err).NotTo(HaveOccurred()) 2962 got := clusterScope.FailureDomains() 2963 g.Expect(tc.expectFailureDomains).Should(ConsistOf(got)) 2964 }) 2965 } 2966 } 2967 2968 func TestClusterScope_LBSpecs(t *testing.T) { 2969 tests := []struct { 2970 name string 2971 azureCluster *infrav1.AzureCluster 2972 want []azure.ResourceSpecGetter 2973 }{ 2974 { 2975 name: "API Server LB, Control Plane Oubound LB, and Node Outbound LB", 2976 azureCluster: &infrav1.AzureCluster{ 2977 ObjectMeta: metav1.ObjectMeta{ 2978 Name: "my-cluster", 2979 }, 2980 Spec: infrav1.AzureClusterSpec{ 2981 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 2982 AdditionalTags: infrav1.Tags{ 2983 "foo": "bar", 2984 }, 2985 SubscriptionID: "123", 2986 Location: "westus2", 2987 IdentityRef: &corev1.ObjectReference{ 2988 Kind: infrav1.AzureClusterIdentityKind, 2989 }, 2990 }, 2991 ResourceGroup: "my-rg", 2992 NetworkSpec: infrav1.NetworkSpec{ 2993 Vnet: infrav1.VnetSpec{ 2994 Name: "my-vnet", 2995 ResourceGroup: "my-rg", 2996 }, 2997 Subnets: []infrav1.SubnetSpec{ 2998 { 2999 SubnetClassSpec: infrav1.SubnetClassSpec{ 3000 Name: "cp-subnet", 3001 Role: infrav1.SubnetControlPlane, 3002 }, 3003 }, 3004 { 3005 SubnetClassSpec: infrav1.SubnetClassSpec{ 3006 Name: "node-subnet", 3007 Role: infrav1.SubnetNode, 3008 }, 3009 }, 3010 }, 3011 APIServerLB: infrav1.LoadBalancerSpec{ 3012 Name: "api-server-lb", 3013 BackendPool: infrav1.BackendPool{ 3014 Name: "api-server-lb-backend-pool", 3015 }, 3016 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 3017 Type: infrav1.Public, 3018 IdleTimeoutInMinutes: ptr.To[int32](30), 3019 SKU: infrav1.SKUStandard, 3020 }, 3021 FrontendIPs: []infrav1.FrontendIP{ 3022 { 3023 Name: "api-server-lb-frontend-ip", 3024 PublicIP: &infrav1.PublicIPSpec{ 3025 Name: "api-server-lb-frontend-ip", 3026 }, 3027 }, 3028 }, 3029 }, 3030 ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{ 3031 Name: "cp-outbound-lb", 3032 BackendPool: infrav1.BackendPool{ 3033 Name: "cp-outbound-backend-pool", 3034 }, 3035 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 3036 Type: infrav1.Public, 3037 IdleTimeoutInMinutes: ptr.To[int32](15), 3038 SKU: infrav1.SKUStandard, 3039 }, 3040 FrontendIPs: []infrav1.FrontendIP{ 3041 { 3042 Name: "cp-outbound-lb-frontend-ip", 3043 PublicIP: &infrav1.PublicIPSpec{ 3044 Name: "cp-outbound-lb-frontend-ip", 3045 }, 3046 }, 3047 }, 3048 }, 3049 NodeOutboundLB: &infrav1.LoadBalancerSpec{ 3050 Name: "node-outbound-lb", 3051 BackendPool: infrav1.BackendPool{ 3052 Name: "node-outbound-backend-pool", 3053 }, 3054 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 3055 Type: infrav1.Public, 3056 IdleTimeoutInMinutes: ptr.To[int32](50), 3057 SKU: infrav1.SKUStandard, 3058 }, 3059 FrontendIPs: []infrav1.FrontendIP{ 3060 { 3061 Name: "node-outbound-lb-frontend-ip", 3062 PublicIP: &infrav1.PublicIPSpec{ 3063 Name: "node-outbound-lb-frontend-ip", 3064 }, 3065 }, 3066 }, 3067 }, 3068 }, 3069 }, 3070 }, 3071 want: []azure.ResourceSpecGetter{ 3072 &loadbalancers.LBSpec{ 3073 Name: "api-server-lb", 3074 ResourceGroup: "my-rg", 3075 SubscriptionID: "123", 3076 ClusterName: "my-cluster", 3077 Location: "westus2", 3078 VNetName: "my-vnet", 3079 VNetResourceGroup: "my-rg", 3080 SubnetName: "cp-subnet", 3081 FrontendIPConfigs: []infrav1.FrontendIP{ 3082 { 3083 Name: "api-server-lb-frontend-ip", 3084 PublicIP: &infrav1.PublicIPSpec{ 3085 Name: "api-server-lb-frontend-ip", 3086 }, 3087 }, 3088 }, 3089 APIServerPort: 6443, 3090 Type: infrav1.Public, 3091 SKU: infrav1.SKUStandard, 3092 Role: infrav1.APIServerRole, 3093 BackendPoolName: "api-server-lb-backend-pool", 3094 IdleTimeoutInMinutes: ptr.To[int32](30), 3095 AdditionalTags: infrav1.Tags{ 3096 "foo": "bar", 3097 }, 3098 }, 3099 &loadbalancers.LBSpec{ 3100 Name: "node-outbound-lb", 3101 ResourceGroup: "my-rg", 3102 SubscriptionID: "123", 3103 ClusterName: "my-cluster", 3104 Location: "westus2", 3105 VNetName: "my-vnet", 3106 VNetResourceGroup: "my-rg", 3107 FrontendIPConfigs: []infrav1.FrontendIP{ 3108 { 3109 Name: "node-outbound-lb-frontend-ip", 3110 PublicIP: &infrav1.PublicIPSpec{ 3111 Name: "node-outbound-lb-frontend-ip", 3112 }, 3113 }, 3114 }, 3115 Type: infrav1.Public, 3116 SKU: infrav1.SKUStandard, 3117 Role: infrav1.NodeOutboundRole, 3118 BackendPoolName: "node-outbound-backend-pool", 3119 IdleTimeoutInMinutes: ptr.To[int32](50), 3120 AdditionalTags: infrav1.Tags{ 3121 "foo": "bar", 3122 }, 3123 }, 3124 &loadbalancers.LBSpec{ 3125 Name: "cp-outbound-lb", 3126 ResourceGroup: "my-rg", 3127 SubscriptionID: "123", 3128 ClusterName: "my-cluster", 3129 Location: "westus2", 3130 VNetName: "my-vnet", 3131 VNetResourceGroup: "my-rg", 3132 FrontendIPConfigs: []infrav1.FrontendIP{ 3133 { 3134 Name: "cp-outbound-lb-frontend-ip", 3135 PublicIP: &infrav1.PublicIPSpec{ 3136 Name: "cp-outbound-lb-frontend-ip", 3137 }, 3138 }, 3139 }, 3140 Type: infrav1.Public, 3141 SKU: infrav1.SKUStandard, 3142 BackendPoolName: "cp-outbound-backend-pool", 3143 IdleTimeoutInMinutes: ptr.To[int32](15), 3144 Role: infrav1.ControlPlaneOutboundRole, 3145 AdditionalTags: infrav1.Tags{ 3146 "foo": "bar", 3147 }, 3148 }, 3149 }, 3150 }, 3151 { 3152 name: "Private API Server LB", 3153 azureCluster: &infrav1.AzureCluster{ 3154 ObjectMeta: metav1.ObjectMeta{ 3155 Name: "my-cluster", 3156 }, 3157 Spec: infrav1.AzureClusterSpec{ 3158 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 3159 SubscriptionID: "123", 3160 Location: "westus2", 3161 IdentityRef: &corev1.ObjectReference{ 3162 Kind: infrav1.AzureClusterIdentityKind, 3163 }, 3164 }, 3165 ResourceGroup: "my-rg", 3166 NetworkSpec: infrav1.NetworkSpec{ 3167 Vnet: infrav1.VnetSpec{ 3168 Name: "my-vnet", 3169 ResourceGroup: "my-rg", 3170 }, 3171 Subnets: []infrav1.SubnetSpec{ 3172 { 3173 SubnetClassSpec: infrav1.SubnetClassSpec{ 3174 Name: "cp-subnet", 3175 Role: infrav1.SubnetControlPlane, 3176 }, 3177 }, 3178 { 3179 SubnetClassSpec: infrav1.SubnetClassSpec{ 3180 Name: "node-subnet", 3181 Role: infrav1.SubnetNode, 3182 }, 3183 }, 3184 }, 3185 APIServerLB: infrav1.LoadBalancerSpec{ 3186 Name: "api-server-lb", 3187 BackendPool: infrav1.BackendPool{ 3188 Name: "api-server-lb-backend-pool", 3189 }, 3190 LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{ 3191 Type: infrav1.Internal, 3192 IdleTimeoutInMinutes: ptr.To[int32](30), 3193 SKU: infrav1.SKUStandard, 3194 }, 3195 }, 3196 }, 3197 }, 3198 }, 3199 want: []azure.ResourceSpecGetter{ 3200 &loadbalancers.LBSpec{ 3201 Name: "api-server-lb", 3202 ResourceGroup: "my-rg", 3203 SubscriptionID: "123", 3204 ClusterName: "my-cluster", 3205 Location: "westus2", 3206 VNetName: "my-vnet", 3207 VNetResourceGroup: "my-rg", 3208 SubnetName: "cp-subnet", 3209 APIServerPort: 6443, 3210 Type: infrav1.Internal, 3211 SKU: infrav1.SKUStandard, 3212 Role: infrav1.APIServerRole, 3213 BackendPoolName: "api-server-lb-backend-pool", 3214 IdleTimeoutInMinutes: ptr.To[int32](30), 3215 AdditionalTags: infrav1.Tags{}, 3216 }, 3217 }, 3218 }, 3219 } 3220 for _, tc := range tests { 3221 tc := tc 3222 t.Run(tc.name, func(t *testing.T) { 3223 t.Parallel() 3224 g := NewWithT(t) 3225 scheme := runtime.NewScheme() 3226 _ = infrav1.AddToScheme(scheme) 3227 _ = clusterv1.AddToScheme(scheme) 3228 _ = corev1.AddToScheme(scheme) 3229 3230 cluster := &clusterv1.Cluster{ 3231 ObjectMeta: metav1.ObjectMeta{ 3232 Name: tc.azureCluster.Name, 3233 Namespace: "default", 3234 }, 3235 } 3236 fakeIdentity := &infrav1.AzureClusterIdentity{ 3237 Spec: infrav1.AzureClusterIdentitySpec{ 3238 Type: infrav1.ServicePrincipal, 3239 ClientID: fakeClientID, 3240 TenantID: fakeTenantID, 3241 }, 3242 } 3243 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 3244 3245 initObjects := []runtime.Object{cluster, tc.azureCluster, fakeIdentity, fakeSecret} 3246 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 3247 3248 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 3249 Cluster: cluster, 3250 AzureCluster: tc.azureCluster, 3251 Client: fakeClient, 3252 }) 3253 g.Expect(err).NotTo(HaveOccurred()) 3254 if got := clusterScope.LBSpecs(); !reflect.DeepEqual(got, tc.want) { 3255 t.Errorf("LBSpecs() diff between expected result and actual result (%v): %s", got, cmp.Diff(tc.want, got)) 3256 } 3257 }) 3258 } 3259 } 3260 3261 func TestExtendedLocationName(t *testing.T) { 3262 tests := []struct { 3263 name string 3264 clusterName string 3265 extendedLocation infrav1.ExtendedLocationSpec 3266 }{ 3267 { 3268 name: "Empty extendedLocatioName", 3269 clusterName: "my-cluster", 3270 extendedLocation: infrav1.ExtendedLocationSpec{ 3271 Name: "", 3272 Type: "", 3273 }, 3274 }, 3275 { 3276 name: "Non empty extendedLocationName", 3277 clusterName: "my-cluster", 3278 extendedLocation: infrav1.ExtendedLocationSpec{ 3279 Name: "ex-loc-name", 3280 Type: "ex-loc-type", 3281 }, 3282 }, 3283 } 3284 for _, tc := range tests { 3285 t.Run(tc.name, func(t *testing.T) { 3286 g := NewWithT(t) 3287 scheme := runtime.NewScheme() 3288 _ = infrav1.AddToScheme(scheme) 3289 _ = clusterv1.AddToScheme(scheme) 3290 _ = corev1.AddToScheme(scheme) 3291 3292 cluster := &clusterv1.Cluster{ 3293 ObjectMeta: metav1.ObjectMeta{ 3294 Name: tc.clusterName, 3295 Namespace: "default", 3296 }, 3297 } 3298 3299 azureCluster := &infrav1.AzureCluster{ 3300 ObjectMeta: metav1.ObjectMeta{ 3301 Name: tc.clusterName, 3302 OwnerReferences: []metav1.OwnerReference{ 3303 { 3304 APIVersion: "cluster.x-k8s.io/v1beta1", 3305 Kind: "Cluster", 3306 Name: "my-cluster", 3307 }, 3308 }, 3309 }, 3310 Spec: infrav1.AzureClusterSpec{ 3311 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 3312 SubscriptionID: "123", 3313 ExtendedLocation: &infrav1.ExtendedLocationSpec{ 3314 Name: tc.extendedLocation.Name, 3315 Type: tc.extendedLocation.Type, 3316 }, 3317 IdentityRef: &corev1.ObjectReference{ 3318 Kind: infrav1.AzureClusterIdentityKind, 3319 }, 3320 }, 3321 }, 3322 } 3323 fakeIdentity := &infrav1.AzureClusterIdentity{ 3324 Spec: infrav1.AzureClusterIdentitySpec{ 3325 Type: infrav1.ServicePrincipal, 3326 ClientID: fakeClientID, 3327 TenantID: fakeTenantID, 3328 }, 3329 } 3330 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 3331 3332 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 3333 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 3334 3335 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 3336 Cluster: cluster, 3337 AzureCluster: azureCluster, 3338 Client: fakeClient, 3339 }) 3340 3341 g.Expect(err).NotTo(HaveOccurred()) 3342 got := clusterScope.ExtendedLocationName() 3343 g.Expect(tc.extendedLocation.Name).Should(Equal(got)) 3344 }) 3345 } 3346 } 3347 3348 func TestExtendedLocationType(t *testing.T) { 3349 tests := []struct { 3350 name string 3351 clusterName string 3352 extendedLocation infrav1.ExtendedLocationSpec 3353 }{ 3354 { 3355 name: "Empty extendedLocatioType", 3356 clusterName: "my-cluster", 3357 extendedLocation: infrav1.ExtendedLocationSpec{ 3358 Name: "", 3359 Type: "", 3360 }, 3361 }, 3362 { 3363 name: "Non empty extendedLocationType", 3364 clusterName: "my-cluster", 3365 extendedLocation: infrav1.ExtendedLocationSpec{ 3366 Name: "ex-loc-name", 3367 Type: "ex-loc-type", 3368 }, 3369 }, 3370 } 3371 for _, tc := range tests { 3372 t.Run(tc.name, func(t *testing.T) { 3373 g := NewWithT(t) 3374 scheme := runtime.NewScheme() 3375 _ = infrav1.AddToScheme(scheme) 3376 _ = clusterv1.AddToScheme(scheme) 3377 _ = corev1.AddToScheme(scheme) 3378 3379 cluster := &clusterv1.Cluster{ 3380 ObjectMeta: metav1.ObjectMeta{ 3381 Name: tc.clusterName, 3382 Namespace: "default", 3383 }, 3384 } 3385 3386 azureCluster := &infrav1.AzureCluster{ 3387 ObjectMeta: metav1.ObjectMeta{ 3388 Name: tc.clusterName, 3389 OwnerReferences: []metav1.OwnerReference{ 3390 { 3391 APIVersion: "cluster.x-k8s.io/v1beta1", 3392 Kind: "Cluster", 3393 Name: "my-cluster", 3394 }, 3395 }, 3396 }, 3397 Spec: infrav1.AzureClusterSpec{ 3398 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 3399 SubscriptionID: "123", 3400 ExtendedLocation: &infrav1.ExtendedLocationSpec{ 3401 Name: tc.extendedLocation.Name, 3402 Type: tc.extendedLocation.Type, 3403 }, 3404 IdentityRef: &corev1.ObjectReference{ 3405 Kind: infrav1.AzureClusterIdentityKind, 3406 }, 3407 }, 3408 }, 3409 } 3410 fakeIdentity := &infrav1.AzureClusterIdentity{ 3411 Spec: infrav1.AzureClusterIdentitySpec{ 3412 Type: infrav1.ServicePrincipal, 3413 ClientID: fakeClientID, 3414 TenantID: fakeTenantID, 3415 }, 3416 } 3417 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 3418 3419 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 3420 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 3421 3422 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 3423 Cluster: cluster, 3424 AzureCluster: azureCluster, 3425 Client: fakeClient, 3426 }) 3427 3428 g.Expect(err).NotTo(HaveOccurred()) 3429 got := clusterScope.ExtendedLocationType() 3430 g.Expect(tc.extendedLocation.Type).Should(Equal(got)) 3431 }) 3432 } 3433 } 3434 3435 func TestVNetPeerings(t *testing.T) { 3436 fakeSubscriptionID := "123" 3437 3438 tests := []struct { 3439 name string 3440 subscriptionID string 3441 azureClusterVNetSpec infrav1.VnetSpec 3442 want []azure.ResourceSpecGetter 3443 }{ 3444 { 3445 name: "VNet peerings are not specified", 3446 subscriptionID: fakeSubscriptionID, 3447 azureClusterVNetSpec: infrav1.VnetSpec{ 3448 ResourceGroup: "rg1", 3449 Name: "vnet1", 3450 }, 3451 want: []azure.ResourceSpecGetter{}, 3452 }, 3453 { 3454 name: "One VNet peering is specified", 3455 subscriptionID: fakeSubscriptionID, 3456 azureClusterVNetSpec: infrav1.VnetSpec{ 3457 ResourceGroup: "rg1", 3458 Name: "vnet1", 3459 Peerings: infrav1.VnetPeerings{ 3460 { 3461 VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{ 3462 ResourceGroup: "rg2", 3463 RemoteVnetName: "vnet2", 3464 }, 3465 }, 3466 }, 3467 }, 3468 want: []azure.ResourceSpecGetter{ 3469 &vnetpeerings.VnetPeeringSpec{ 3470 PeeringName: "vnet1-To-vnet2", 3471 SourceResourceGroup: "rg1", 3472 SourceVnetName: "vnet1", 3473 RemoteResourceGroup: "rg2", 3474 RemoteVnetName: "vnet2", 3475 SubscriptionID: fakeSubscriptionID, 3476 }, 3477 &vnetpeerings.VnetPeeringSpec{ 3478 PeeringName: "vnet2-To-vnet1", 3479 SourceResourceGroup: "rg2", 3480 SourceVnetName: "vnet2", 3481 RemoteResourceGroup: "rg1", 3482 RemoteVnetName: "vnet1", 3483 SubscriptionID: fakeSubscriptionID, 3484 }, 3485 }, 3486 }, 3487 { 3488 name: "One VNet peering with optional properties is specified", 3489 subscriptionID: fakeSubscriptionID, 3490 azureClusterVNetSpec: infrav1.VnetSpec{ 3491 ResourceGroup: "rg1", 3492 Name: "vnet1", 3493 Peerings: infrav1.VnetPeerings{ 3494 { 3495 VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{ 3496 ResourceGroup: "rg2", 3497 RemoteVnetName: "vnet2", 3498 ForwardPeeringProperties: infrav1.VnetPeeringProperties{ 3499 AllowForwardedTraffic: ptr.To(true), 3500 AllowGatewayTransit: ptr.To(false), 3501 UseRemoteGateways: ptr.To(true), 3502 }, 3503 ReversePeeringProperties: infrav1.VnetPeeringProperties{ 3504 AllowForwardedTraffic: ptr.To(true), 3505 AllowGatewayTransit: ptr.To(true), 3506 UseRemoteGateways: ptr.To(false), 3507 }, 3508 }, 3509 }, 3510 }, 3511 }, 3512 want: []azure.ResourceSpecGetter{ 3513 &vnetpeerings.VnetPeeringSpec{ 3514 PeeringName: "vnet1-To-vnet2", 3515 SourceResourceGroup: "rg1", 3516 SourceVnetName: "vnet1", 3517 RemoteResourceGroup: "rg2", 3518 RemoteVnetName: "vnet2", 3519 SubscriptionID: fakeSubscriptionID, 3520 AllowForwardedTraffic: ptr.To(true), 3521 AllowGatewayTransit: ptr.To(false), 3522 UseRemoteGateways: ptr.To(true), 3523 }, 3524 &vnetpeerings.VnetPeeringSpec{ 3525 PeeringName: "vnet2-To-vnet1", 3526 SourceResourceGroup: "rg2", 3527 SourceVnetName: "vnet2", 3528 RemoteResourceGroup: "rg1", 3529 RemoteVnetName: "vnet1", 3530 SubscriptionID: fakeSubscriptionID, 3531 AllowForwardedTraffic: ptr.To(true), 3532 AllowGatewayTransit: ptr.To(true), 3533 UseRemoteGateways: ptr.To(false), 3534 }, 3535 }, 3536 }, 3537 { 3538 name: "Two VNet peerings are specified", 3539 subscriptionID: fakeSubscriptionID, 3540 azureClusterVNetSpec: infrav1.VnetSpec{ 3541 ResourceGroup: "rg1", 3542 Name: "vnet1", 3543 Peerings: infrav1.VnetPeerings{ 3544 { 3545 VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{ 3546 ResourceGroup: "rg2", 3547 RemoteVnetName: "vnet2", 3548 ForwardPeeringProperties: infrav1.VnetPeeringProperties{ 3549 AllowForwardedTraffic: ptr.To(true), 3550 AllowGatewayTransit: ptr.To(false), 3551 UseRemoteGateways: ptr.To(true), 3552 }, 3553 ReversePeeringProperties: infrav1.VnetPeeringProperties{ 3554 AllowForwardedTraffic: ptr.To(true), 3555 AllowGatewayTransit: ptr.To(true), 3556 UseRemoteGateways: ptr.To(false), 3557 }, 3558 }, 3559 }, 3560 { 3561 VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{ 3562 ResourceGroup: "rg3", 3563 RemoteVnetName: "vnet3", 3564 }, 3565 }, 3566 }, 3567 }, 3568 want: []azure.ResourceSpecGetter{ 3569 &vnetpeerings.VnetPeeringSpec{ 3570 PeeringName: "vnet1-To-vnet2", 3571 SourceResourceGroup: "rg1", 3572 SourceVnetName: "vnet1", 3573 RemoteResourceGroup: "rg2", 3574 RemoteVnetName: "vnet2", 3575 SubscriptionID: fakeSubscriptionID, 3576 AllowForwardedTraffic: ptr.To(true), 3577 AllowGatewayTransit: ptr.To(false), 3578 UseRemoteGateways: ptr.To(true), 3579 }, 3580 &vnetpeerings.VnetPeeringSpec{ 3581 PeeringName: "vnet2-To-vnet1", 3582 SourceResourceGroup: "rg2", 3583 SourceVnetName: "vnet2", 3584 RemoteResourceGroup: "rg1", 3585 RemoteVnetName: "vnet1", 3586 SubscriptionID: fakeSubscriptionID, 3587 AllowForwardedTraffic: ptr.To(true), 3588 AllowGatewayTransit: ptr.To(true), 3589 UseRemoteGateways: ptr.To(false), 3590 }, 3591 &vnetpeerings.VnetPeeringSpec{ 3592 PeeringName: "vnet1-To-vnet3", 3593 SourceResourceGroup: "rg1", 3594 SourceVnetName: "vnet1", 3595 RemoteResourceGroup: "rg3", 3596 RemoteVnetName: "vnet3", 3597 SubscriptionID: fakeSubscriptionID, 3598 }, 3599 &vnetpeerings.VnetPeeringSpec{ 3600 PeeringName: "vnet3-To-vnet1", 3601 SourceResourceGroup: "rg3", 3602 SourceVnetName: "vnet3", 3603 RemoteResourceGroup: "rg1", 3604 RemoteVnetName: "vnet1", 3605 SubscriptionID: fakeSubscriptionID, 3606 }, 3607 }, 3608 }, 3609 } 3610 3611 for _, tc := range tests { 3612 t.Run(tc.name, func(t *testing.T) { 3613 g := NewWithT(t) 3614 scheme := runtime.NewScheme() 3615 _ = infrav1.AddToScheme(scheme) 3616 _ = clusterv1.AddToScheme(scheme) 3617 _ = corev1.AddToScheme(scheme) 3618 3619 clusterName := "my-cluster" 3620 clusterNamespace := "default" 3621 3622 cluster := &clusterv1.Cluster{ 3623 ObjectMeta: metav1.ObjectMeta{ 3624 Name: clusterName, 3625 Namespace: clusterNamespace, 3626 }, 3627 } 3628 azureCluster := &infrav1.AzureCluster{ 3629 ObjectMeta: metav1.ObjectMeta{ 3630 Name: clusterName, 3631 Namespace: clusterNamespace, 3632 OwnerReferences: []metav1.OwnerReference{ 3633 { 3634 APIVersion: "cluster.x-k8s.io/v1beta1", 3635 Kind: "Cluster", 3636 Name: clusterName, 3637 }, 3638 }, 3639 }, 3640 Spec: infrav1.AzureClusterSpec{ 3641 ResourceGroup: "rg1", 3642 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 3643 SubscriptionID: tc.subscriptionID, 3644 IdentityRef: &corev1.ObjectReference{ 3645 Kind: infrav1.AzureClusterIdentityKind, 3646 }, 3647 }, 3648 NetworkSpec: infrav1.NetworkSpec{ 3649 Vnet: tc.azureClusterVNetSpec, 3650 }, 3651 }, 3652 } 3653 fakeIdentity := &infrav1.AzureClusterIdentity{ 3654 ObjectMeta: metav1.ObjectMeta{ 3655 Namespace: clusterNamespace, 3656 }, 3657 Spec: infrav1.AzureClusterIdentitySpec{ 3658 Type: infrav1.ServicePrincipal, 3659 ClientID: fakeClientID, 3660 TenantID: fakeTenantID, 3661 }, 3662 } 3663 fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}} 3664 3665 initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret} 3666 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build() 3667 3668 clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{ 3669 Cluster: cluster, 3670 AzureCluster: azureCluster, 3671 Client: fakeClient, 3672 }) 3673 g.Expect(err).NotTo(HaveOccurred()) 3674 got := clusterScope.VnetPeeringSpecs() 3675 g.Expect(tc.want).To(Equal(got)) 3676 }) 3677 } 3678 } 3679 3680 func TestPrivateEndpointSpecs(t *testing.T) { 3681 tests := []struct { 3682 name string 3683 clusterScope ClusterScope 3684 want []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint] 3685 }{ 3686 { 3687 name: "returns empty private endpoints list if no subnets are specified", 3688 clusterScope: ClusterScope{ 3689 AzureCluster: &infrav1.AzureCluster{ 3690 Spec: infrav1.AzureClusterSpec{ 3691 NetworkSpec: infrav1.NetworkSpec{ 3692 Subnets: infrav1.Subnets{}, 3693 }, 3694 }, 3695 }, 3696 cache: &ClusterCache{}, 3697 }, 3698 want: make([]azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint], 0), 3699 }, 3700 { 3701 name: "returns empty private endpoints list if no private endpoints are specified", 3702 clusterScope: ClusterScope{ 3703 AzureCluster: &infrav1.AzureCluster{ 3704 Spec: infrav1.AzureClusterSpec{ 3705 NetworkSpec: infrav1.NetworkSpec{ 3706 Subnets: []infrav1.SubnetSpec{ 3707 { 3708 SubnetClassSpec: infrav1.SubnetClassSpec{ 3709 PrivateEndpoints: infrav1.PrivateEndpoints{}, 3710 }, 3711 }, 3712 }, 3713 }, 3714 }, 3715 }, 3716 cache: &ClusterCache{}, 3717 }, 3718 want: make([]azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint], 0), 3719 }, 3720 { 3721 name: "returns list of private endpoint specs if private endpoints are specified", 3722 clusterScope: ClusterScope{ 3723 Cluster: &clusterv1.Cluster{ 3724 ObjectMeta: metav1.ObjectMeta{ 3725 Name: "my-cluster", 3726 Namespace: "dummy-ns", 3727 }, 3728 }, 3729 AzureCluster: &infrav1.AzureCluster{ 3730 Spec: infrav1.AzureClusterSpec{ 3731 ResourceGroup: "dummy-rg", 3732 NetworkSpec: infrav1.NetworkSpec{ 3733 Subnets: []infrav1.SubnetSpec{ 3734 { 3735 ID: "dummy-subnet-id", 3736 SubnetClassSpec: infrav1.SubnetClassSpec{ 3737 PrivateEndpoints: []infrav1.PrivateEndpointSpec{ 3738 { 3739 Name: "my-private-endpoint", 3740 Location: "westus2", 3741 CustomNetworkInterfaceName: "my-custom-nic", 3742 PrivateIPAddresses: []string{ 3743 "IP1", 3744 "IP2", 3745 }, 3746 ApplicationSecurityGroups: []string{ 3747 "ASG1", 3748 "ASG2", 3749 }, 3750 PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{ 3751 { 3752 Name: "my-pls-connection", 3753 RequestMessage: "my-request-message", 3754 PrivateLinkServiceID: "my-pls-id", 3755 GroupIDs: []string{ 3756 "my-group-id-1", 3757 }, 3758 }, 3759 }, 3760 }, 3761 { 3762 Name: "my-private-endpoint-2", 3763 Location: "westus2", 3764 CustomNetworkInterfaceName: "my-custom-nic-2", 3765 PrivateIPAddresses: []string{ 3766 "IP3", 3767 "IP4", 3768 }, 3769 ApplicationSecurityGroups: []string{ 3770 "ASG3", 3771 "ASG4", 3772 }, 3773 PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{ 3774 { 3775 Name: "my-pls-connection", 3776 RequestMessage: "my-request-message", 3777 PrivateLinkServiceID: "my-pls-id", 3778 GroupIDs: []string{ 3779 "my-group-id-1", 3780 }, 3781 }, 3782 }, 3783 }, 3784 }, 3785 }, 3786 }, 3787 { 3788 ID: "dummy-subnet-id-2", 3789 SubnetClassSpec: infrav1.SubnetClassSpec{ 3790 PrivateEndpoints: []infrav1.PrivateEndpointSpec{ 3791 { 3792 Name: "my-private-endpoint-3", 3793 Location: "westus2", 3794 CustomNetworkInterfaceName: "my-custom-nic-3", 3795 PrivateIPAddresses: []string{ 3796 "IP5", 3797 "IP6", 3798 }, 3799 ApplicationSecurityGroups: []string{ 3800 "ASG5", 3801 "ASG6", 3802 }, 3803 PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{ 3804 { 3805 Name: "my-pls-connection", 3806 RequestMessage: "my-request-message", 3807 PrivateLinkServiceID: "my-pls-id", 3808 GroupIDs: []string{ 3809 "my-group-id-1", 3810 }, 3811 }, 3812 }, 3813 }, 3814 }, 3815 }, 3816 }, 3817 }, 3818 }, 3819 }, 3820 }, 3821 cache: &ClusterCache{}, 3822 }, 3823 want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint]{ 3824 &privateendpoints.PrivateEndpointSpec{ 3825 Name: "my-private-endpoint", 3826 ResourceGroup: "dummy-rg", 3827 Location: "westus2", 3828 CustomNetworkInterfaceName: "my-custom-nic", 3829 PrivateIPAddresses: []string{ 3830 "IP1", 3831 "IP2", 3832 }, 3833 SubnetID: "dummy-subnet-id", 3834 ApplicationSecurityGroups: []string{ 3835 "ASG1", 3836 "ASG2", 3837 }, 3838 ClusterName: "my-cluster", 3839 PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{ 3840 { 3841 Name: "my-pls-connection", 3842 RequestMessage: "my-request-message", 3843 PrivateLinkServiceID: "my-pls-id", 3844 GroupIDs: []string{ 3845 "my-group-id-1", 3846 }, 3847 }, 3848 }, 3849 AdditionalTags: make(infrav1.Tags, 0), 3850 }, 3851 &privateendpoints.PrivateEndpointSpec{ 3852 Name: "my-private-endpoint-2", 3853 ResourceGroup: "dummy-rg", 3854 Location: "westus2", 3855 CustomNetworkInterfaceName: "my-custom-nic-2", 3856 PrivateIPAddresses: []string{ 3857 "IP3", 3858 "IP4", 3859 }, 3860 SubnetID: "dummy-subnet-id", 3861 ApplicationSecurityGroups: []string{ 3862 "ASG3", 3863 "ASG4", 3864 }, 3865 ClusterName: "my-cluster", 3866 PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{ 3867 { 3868 Name: "my-pls-connection", 3869 RequestMessage: "my-request-message", 3870 PrivateLinkServiceID: "my-pls-id", 3871 GroupIDs: []string{ 3872 "my-group-id-1", 3873 }, 3874 }, 3875 }, 3876 AdditionalTags: make(infrav1.Tags, 0), 3877 }, 3878 &privateendpoints.PrivateEndpointSpec{ 3879 Name: "my-private-endpoint-3", 3880 ResourceGroup: "dummy-rg", 3881 Location: "westus2", 3882 CustomNetworkInterfaceName: "my-custom-nic-3", 3883 PrivateIPAddresses: []string{ 3884 "IP5", 3885 "IP6", 3886 }, 3887 SubnetID: "dummy-subnet-id-2", 3888 ApplicationSecurityGroups: []string{ 3889 "ASG5", 3890 "ASG6", 3891 }, 3892 ClusterName: "my-cluster", 3893 PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{ 3894 { 3895 Name: "my-pls-connection", 3896 RequestMessage: "my-request-message", 3897 PrivateLinkServiceID: "my-pls-id", 3898 GroupIDs: []string{ 3899 "my-group-id-1", 3900 }, 3901 }, 3902 }, 3903 AdditionalTags: make(infrav1.Tags, 0), 3904 }, 3905 }, 3906 }, 3907 } 3908 3909 for _, tt := range tests { 3910 tt := tt 3911 t.Run(tt.name, func(t *testing.T) { 3912 t.Parallel() 3913 if got := tt.clusterScope.PrivateEndpointSpecs(); !reflect.DeepEqual(got, tt.want) { 3914 t.Errorf("PrivateEndpointSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want)) 3915 } 3916 }) 3917 } 3918 } 3919 3920 func TestSetFailureDomain(t *testing.T) { 3921 t.Parallel() 3922 3923 cases := map[string]struct { 3924 discoveredFDs clusterv1.FailureDomains 3925 specifiedFDs clusterv1.FailureDomains 3926 expectedFDs clusterv1.FailureDomains 3927 }{ 3928 "no failure domains specified": { 3929 discoveredFDs: clusterv1.FailureDomains{ 3930 "fd1": clusterv1.FailureDomainSpec{ControlPlane: true}, 3931 "fd2": clusterv1.FailureDomainSpec{ControlPlane: false}, 3932 }, 3933 expectedFDs: clusterv1.FailureDomains{ 3934 "fd1": clusterv1.FailureDomainSpec{ControlPlane: true}, 3935 "fd2": clusterv1.FailureDomainSpec{ControlPlane: false}, 3936 }, 3937 }, 3938 "no failure domains discovered": { 3939 specifiedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}}, 3940 }, 3941 "failure domain specified without intersection": { 3942 discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}}, 3943 specifiedFDs: clusterv1.FailureDomains{"fd2": clusterv1.FailureDomainSpec{ControlPlane: false}}, 3944 expectedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}}, 3945 }, 3946 "failure domain override to false succeeds": { 3947 discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}}, 3948 specifiedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}}, 3949 expectedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}}, 3950 }, 3951 "failure domain override to true fails": { 3952 discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}}, 3953 specifiedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}}, 3954 expectedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}}, 3955 }, 3956 } 3957 3958 for name, tc := range cases { 3959 tc := tc 3960 t.Run(name, func(t *testing.T) { 3961 t.Parallel() 3962 g := NewWithT(t) 3963 3964 c := ClusterScope{ 3965 AzureCluster: &infrav1.AzureCluster{ 3966 Spec: infrav1.AzureClusterSpec{ 3967 AzureClusterClassSpec: infrav1.AzureClusterClassSpec{ 3968 FailureDomains: tc.specifiedFDs, 3969 IdentityRef: &corev1.ObjectReference{ 3970 Kind: infrav1.AzureClusterIdentityKind, 3971 }, 3972 }, 3973 }, 3974 }, 3975 } 3976 3977 for fdName, fd := range tc.discoveredFDs { 3978 c.SetFailureDomain(fdName, fd) 3979 } 3980 3981 for fdName, fd := range tc.expectedFDs { 3982 g.Expect(fdName).Should(BeKeyOf(c.AzureCluster.Status.FailureDomains)) 3983 g.Expect(c.AzureCluster.Status.FailureDomains[fdName].ControlPlane).To(Equal(fd.ControlPlane)) 3984 3985 delete(c.AzureCluster.Status.FailureDomains, fdName) 3986 } 3987 3988 g.Expect(c.AzureCluster.Status.FailureDomains).To(BeEmpty()) 3989 }) 3990 } 3991 } 3992 3993 func TestGroupSpecs(t *testing.T) { 3994 cases := []struct { 3995 name string 3996 input ClusterScope 3997 expected []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup] 3998 }{ 3999 { 4000 name: "virtualNetwork belongs to a different resource group", 4001 input: ClusterScope{ 4002 Cluster: &clusterv1.Cluster{ 4003 ObjectMeta: metav1.ObjectMeta{ 4004 Name: "cluster1", 4005 }, 4006 }, 4007 AzureCluster: &infrav1.AzureCluster{ 4008 Spec: infrav1.AzureClusterSpec{ 4009 ResourceGroup: "dummy-rg", 4010 NetworkSpec: infrav1.NetworkSpec{ 4011 Vnet: infrav1.VnetSpec{ 4012 ResourceGroup: "different-rg", 4013 }, 4014 }, 4015 }, 4016 }, 4017 }, 4018 expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{ 4019 &groups.GroupSpec{ 4020 Name: "dummy-rg", 4021 AzureName: "dummy-rg", 4022 ClusterName: "cluster1", 4023 Location: "", 4024 AdditionalTags: make(infrav1.Tags, 0), 4025 }, 4026 &groups.GroupSpec{ 4027 Name: "different-rg", 4028 AzureName: "different-rg", 4029 ClusterName: "cluster1", 4030 Location: "", 4031 AdditionalTags: make(infrav1.Tags, 0), 4032 }, 4033 }, 4034 }, 4035 { 4036 name: "virtualNetwork belongs to a same resource group", 4037 input: ClusterScope{ 4038 Cluster: &clusterv1.Cluster{ 4039 ObjectMeta: metav1.ObjectMeta{ 4040 Name: "cluster1", 4041 }, 4042 }, 4043 AzureCluster: &infrav1.AzureCluster{ 4044 Spec: infrav1.AzureClusterSpec{ 4045 ResourceGroup: "dummy-rg", 4046 NetworkSpec: infrav1.NetworkSpec{ 4047 Vnet: infrav1.VnetSpec{ 4048 ResourceGroup: "dummy-rg", 4049 }, 4050 }, 4051 }, 4052 }, 4053 }, 4054 expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{ 4055 &groups.GroupSpec{ 4056 Name: "dummy-rg", 4057 AzureName: "dummy-rg", 4058 ClusterName: "cluster1", 4059 Location: "", 4060 AdditionalTags: make(infrav1.Tags, 0), 4061 }, 4062 }, 4063 }, 4064 { 4065 name: "virtualNetwork resource group not specified", 4066 input: ClusterScope{ 4067 Cluster: &clusterv1.Cluster{ 4068 ObjectMeta: metav1.ObjectMeta{ 4069 Name: "cluster1", 4070 Namespace: "default", 4071 }, 4072 }, 4073 AzureCluster: &infrav1.AzureCluster{ 4074 Spec: infrav1.AzureClusterSpec{ 4075 ResourceGroup: "dummy-rg", 4076 NetworkSpec: infrav1.NetworkSpec{ 4077 Vnet: infrav1.VnetSpec{ 4078 Name: "vnet1", 4079 }, 4080 }, 4081 }, 4082 }, 4083 }, 4084 expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{ 4085 &groups.GroupSpec{ 4086 Name: "dummy-rg", 4087 AzureName: "dummy-rg", 4088 ClusterName: "cluster1", 4089 Location: "", 4090 AdditionalTags: make(infrav1.Tags, 0), 4091 }, 4092 }, 4093 }, 4094 { 4095 name: "virtualNetwork belongs to different resource group with non-k8s name", 4096 input: ClusterScope{ 4097 Cluster: &clusterv1.Cluster{ 4098 ObjectMeta: metav1.ObjectMeta{ 4099 Name: "cluster1", 4100 Namespace: "default", 4101 }, 4102 }, 4103 AzureCluster: &infrav1.AzureCluster{ 4104 Spec: infrav1.AzureClusterSpec{ 4105 ResourceGroup: "dummy-rg", 4106 NetworkSpec: infrav1.NetworkSpec{ 4107 Vnet: infrav1.VnetSpec{ 4108 ResourceGroup: "my_custom_rg", 4109 Name: "vnet1", 4110 }, 4111 }, 4112 }, 4113 }, 4114 }, 4115 expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{ 4116 &groups.GroupSpec{ 4117 Name: "dummy-rg", 4118 AzureName: "dummy-rg", 4119 ClusterName: "cluster1", 4120 Location: "", 4121 AdditionalTags: make(infrav1.Tags, 0), 4122 }, 4123 &groups.GroupSpec{ 4124 Name: "my-custom-rg", 4125 AzureName: "my_custom_rg", 4126 ClusterName: "cluster1", 4127 Location: "", 4128 AdditionalTags: make(infrav1.Tags, 0), 4129 }, 4130 }, 4131 }, 4132 } 4133 4134 for _, c := range cases { 4135 c := c 4136 t.Run(c.name, func(t *testing.T) { 4137 s := &ClusterScope{ 4138 AzureCluster: c.input.AzureCluster, 4139 Cluster: c.input.Cluster, 4140 } 4141 if got := s.GroupSpecs(); !reflect.DeepEqual(got, c.expected) { 4142 t.Errorf("GroupSpecs() = %s, want %s", specArrayToString(got), specArrayToString(c.expected)) 4143 } 4144 }) 4145 } 4146 }