github.com/openshift/installer@v1.4.17/pkg/infrastructure/azure/network.go (about) 1 package azure 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 8 "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" 9 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" 10 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2" 11 "k8s.io/utils/ptr" 12 ) 13 14 type lbInput struct { 15 loadBalancerName string 16 infraID string 17 region string 18 resourceGroup string 19 subscriptionID string 20 frontendIPConfigName string 21 backendAddressPoolName string 22 idPrefix string 23 lbClient *armnetwork.LoadBalancersClient 24 tags map[string]*string 25 } 26 27 type pipInput struct { 28 infraID string 29 name string 30 region string 31 resourceGroup string 32 pipClient *armnetwork.PublicIPAddressesClient 33 tags map[string]*string 34 } 35 36 type vmInput struct { 37 infraID string 38 resourceGroup string 39 ids []string 40 bap *armnetwork.BackendAddressPool 41 vmClient *armcompute.VirtualMachinesClient 42 nicClient *armnetwork.InterfacesClient 43 } 44 45 type securityGroupInput struct { 46 resourceGroupName string 47 securityGroupName string 48 securityRuleName string 49 securityRulePort string 50 securityRulePriority int32 51 networkClientFactory *armnetwork.ClientFactory 52 } 53 54 type inboundNatRuleInput struct { 55 resourceGroupName string 56 loadBalancerName string 57 bootstrapNicName string 58 frontendIPConfigID string 59 inboundNatRuleID string 60 inboundNatRuleName string 61 inboundNatRulePort int32 62 networkClientFactory *armnetwork.ClientFactory 63 } 64 65 func createPublicIP(ctx context.Context, in *pipInput) (*armnetwork.PublicIPAddress, error) { 66 pollerResp, err := in.pipClient.BeginCreateOrUpdate( 67 ctx, 68 in.resourceGroup, 69 in.name, 70 armnetwork.PublicIPAddress{ 71 Name: to.Ptr(in.name), 72 Location: to.Ptr(in.region), 73 SKU: &armnetwork.PublicIPAddressSKU{ 74 Name: to.Ptr(armnetwork.PublicIPAddressSKUNameStandard), 75 Tier: to.Ptr(armnetwork.PublicIPAddressSKUTierRegional), 76 }, 77 Properties: &armnetwork.PublicIPAddressPropertiesFormat{ 78 PublicIPAddressVersion: to.Ptr(armnetwork.IPVersionIPv4), 79 PublicIPAllocationMethod: to.Ptr(armnetwork.IPAllocationMethodStatic), 80 DNSSettings: &armnetwork.PublicIPAddressDNSSettings{ 81 DomainNameLabel: to.Ptr(in.infraID), 82 }, 83 }, 84 Tags: in.tags, 85 }, 86 nil, 87 ) 88 if err != nil { 89 return nil, err 90 } 91 92 resp, err := pollerResp.PollUntilDone(ctx, nil) 93 if err != nil { 94 return nil, err 95 } 96 return &resp.PublicIPAddress, nil 97 } 98 99 func createAPILoadBalancer(ctx context.Context, pip *armnetwork.PublicIPAddress, in *lbInput) (*armnetwork.LoadBalancer, error) { 100 probeName := "api-probe" 101 102 pollerResp, err := in.lbClient.BeginCreateOrUpdate(ctx, 103 in.resourceGroup, 104 in.loadBalancerName, 105 armnetwork.LoadBalancer{ 106 Location: to.Ptr(in.region), 107 SKU: &armnetwork.LoadBalancerSKU{ 108 Name: to.Ptr(armnetwork.LoadBalancerSKUNameStandard), 109 Tier: to.Ptr(armnetwork.LoadBalancerSKUTierRegional), 110 }, 111 Properties: &armnetwork.LoadBalancerPropertiesFormat{ 112 FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{ 113 { 114 Name: &in.frontendIPConfigName, 115 Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{ 116 PrivateIPAllocationMethod: to.Ptr(armnetwork.IPAllocationMethodDynamic), 117 PublicIPAddress: pip, 118 }, 119 }, 120 }, 121 BackendAddressPools: []*armnetwork.BackendAddressPool{ 122 { 123 Name: &in.backendAddressPoolName, 124 }, 125 }, 126 Probes: []*armnetwork.Probe{ 127 { 128 Name: &probeName, 129 Properties: &armnetwork.ProbePropertiesFormat{ 130 Protocol: to.Ptr(armnetwork.ProbeProtocolHTTPS), 131 Port: to.Ptr[int32](6443), 132 IntervalInSeconds: to.Ptr[int32](5), 133 NumberOfProbes: to.Ptr[int32](2), 134 RequestPath: to.Ptr("/readyz"), 135 }, 136 }, 137 }, 138 LoadBalancingRules: []*armnetwork.LoadBalancingRule{ 139 { 140 Name: to.Ptr("api-v4"), 141 Properties: &armnetwork.LoadBalancingRulePropertiesFormat{ 142 Protocol: to.Ptr(armnetwork.TransportProtocolTCP), 143 FrontendPort: to.Ptr[int32](6443), 144 BackendPort: to.Ptr[int32](6443), 145 IdleTimeoutInMinutes: to.Ptr[int32](30), 146 EnableFloatingIP: to.Ptr(false), 147 LoadDistribution: to.Ptr(armnetwork.LoadDistributionDefault), 148 FrontendIPConfiguration: &armnetwork.SubResource{ 149 ID: to.Ptr(fmt.Sprintf("/%s/%s/frontendIPConfigurations/%s", in.idPrefix, in.loadBalancerName, in.frontendIPConfigName)), 150 }, 151 BackendAddressPool: &armnetwork.SubResource{ 152 ID: to.Ptr(fmt.Sprintf("/%s/%s/backendAddressPools/%s", in.idPrefix, in.loadBalancerName, in.backendAddressPoolName)), 153 }, 154 Probe: &armnetwork.SubResource{ 155 ID: to.Ptr(fmt.Sprintf("/%s/%s/probes/%s", in.idPrefix, in.loadBalancerName, probeName)), 156 }, 157 }, 158 }, 159 }, 160 }, 161 Tags: in.tags, 162 }, nil) 163 164 if err != nil { 165 return nil, fmt.Errorf("cannot create load balancer: %w", err) 166 } 167 168 resp, err := pollerResp.PollUntilDone(ctx, nil) 169 if err != nil { 170 return nil, err 171 } 172 return &resp.LoadBalancer, nil 173 } 174 175 func updateOutboundLoadBalancerToAPILoadBalancer(ctx context.Context, pip *armnetwork.PublicIPAddress, in *lbInput) (*armnetwork.LoadBalancer, error) { 176 probeName := "api-probe" 177 178 // Get the CAPI-created outbound load balancer so we can modify it. 179 extLB, err := in.lbClient.Get(ctx, in.resourceGroup, in.loadBalancerName, nil) 180 if err != nil { 181 return nil, fmt.Errorf("failed to get external load balancer: %w", err) 182 } 183 184 // Get the existing frontend configuration and backend address pool and 185 // create an additional frontend configuration and backend address 186 // pool. Use the newly created public IP address with the additional 187 // configuration so we can setup load balancing rules for the external 188 // API server. 189 extLB.Properties.FrontendIPConfigurations = append(extLB.Properties.FrontendIPConfigurations, 190 &armnetwork.FrontendIPConfiguration{ 191 Name: &in.frontendIPConfigName, 192 Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{ 193 PrivateIPAllocationMethod: to.Ptr(armnetwork.IPAllocationMethodDynamic), 194 PublicIPAddress: pip, 195 }, 196 }) 197 extLB.Properties.BackendAddressPools = append(extLB.Properties.BackendAddressPools, 198 &armnetwork.BackendAddressPool{ 199 Name: &in.backendAddressPoolName, 200 }) 201 202 pollerResp, err := in.lbClient.BeginCreateOrUpdate(ctx, 203 in.resourceGroup, 204 in.loadBalancerName, 205 armnetwork.LoadBalancer{ 206 Location: to.Ptr(in.region), 207 SKU: &armnetwork.LoadBalancerSKU{ 208 Name: to.Ptr(armnetwork.LoadBalancerSKUNameStandard), 209 Tier: to.Ptr(armnetwork.LoadBalancerSKUTierRegional), 210 }, 211 Properties: &armnetwork.LoadBalancerPropertiesFormat{ 212 FrontendIPConfigurations: extLB.Properties.FrontendIPConfigurations, 213 BackendAddressPools: extLB.Properties.BackendAddressPools, 214 Probes: []*armnetwork.Probe{ 215 { 216 Name: &probeName, 217 Properties: &armnetwork.ProbePropertiesFormat{ 218 Protocol: to.Ptr(armnetwork.ProbeProtocolHTTPS), 219 Port: to.Ptr[int32](6443), 220 IntervalInSeconds: to.Ptr[int32](5), 221 NumberOfProbes: to.Ptr[int32](2), 222 RequestPath: to.Ptr("/readyz"), 223 }, 224 }, 225 }, 226 LoadBalancingRules: []*armnetwork.LoadBalancingRule{ 227 { 228 Name: to.Ptr("api-v4"), 229 Properties: &armnetwork.LoadBalancingRulePropertiesFormat{ 230 Protocol: to.Ptr(armnetwork.TransportProtocolTCP), 231 FrontendPort: to.Ptr[int32](6443), 232 BackendPort: to.Ptr[int32](6443), 233 IdleTimeoutInMinutes: to.Ptr[int32](30), 234 EnableFloatingIP: to.Ptr(false), 235 LoadDistribution: to.Ptr(armnetwork.LoadDistributionDefault), 236 FrontendIPConfiguration: &armnetwork.SubResource{ 237 ID: to.Ptr(fmt.Sprintf("/%s/%s/frontendIPConfigurations/%s", in.idPrefix, in.loadBalancerName, in.frontendIPConfigName)), 238 }, 239 BackendAddressPool: &armnetwork.SubResource{ 240 ID: to.Ptr(fmt.Sprintf("/%s/%s/backendAddressPools/%s", in.idPrefix, in.loadBalancerName, in.backendAddressPoolName)), 241 }, 242 Probe: &armnetwork.SubResource{ 243 ID: to.Ptr(fmt.Sprintf("/%s/%s/probes/%s", in.idPrefix, in.loadBalancerName, probeName)), 244 }, 245 }, 246 }, 247 }, 248 OutboundRules: extLB.Properties.OutboundRules, 249 }, 250 Tags: in.tags, 251 }, nil) 252 253 if err != nil { 254 return nil, fmt.Errorf("cannot update load balancer: %w", err) 255 } 256 257 resp, err := pollerResp.PollUntilDone(ctx, nil) 258 if err != nil { 259 return nil, err 260 } 261 return &resp.LoadBalancer, nil 262 } 263 264 func updateInternalLoadBalancer(ctx context.Context, in *lbInput) (*armnetwork.LoadBalancer, error) { 265 mcsProbeName := "sint-probe" 266 267 // Get the CAPI-created internal load balancer so we can modify it. 268 lbResp, err := in.lbClient.Get(ctx, in.resourceGroup, in.loadBalancerName, nil) 269 if err != nil { 270 return nil, fmt.Errorf("could not get internal load balancer: %w", err) 271 } 272 intLB := lbResp.LoadBalancer 273 274 mcsProbe := &armnetwork.Probe{ 275 Name: to.Ptr(mcsProbeName), 276 Properties: &armnetwork.ProbePropertiesFormat{ 277 Protocol: to.Ptr(armnetwork.ProbeProtocolHTTPS), 278 Port: to.Ptr[int32](22623), 279 IntervalInSeconds: to.Ptr[int32](5), 280 NumberOfProbes: to.Ptr[int32](2), 281 RequestPath: to.Ptr("/healthz"), 282 }, 283 } 284 285 existingFrontEndIPConfig := intLB.Properties.FrontendIPConfigurations 286 if len(existingFrontEndIPConfig) == 0 { 287 return nil, fmt.Errorf("could not get frontEndIPConfig for internal LB %s", *intLB.Name) 288 } 289 existingFrontEndIPConfigName := *(existingFrontEndIPConfig[0].Name) 290 291 mcsRule := &armnetwork.LoadBalancingRule{ 292 Name: to.Ptr("sint-v4"), 293 Properties: &armnetwork.LoadBalancingRulePropertiesFormat{ 294 Protocol: to.Ptr(armnetwork.TransportProtocolTCP), 295 FrontendPort: to.Ptr[int32](22623), 296 BackendPort: to.Ptr[int32](22623), 297 IdleTimeoutInMinutes: to.Ptr[int32](30), 298 EnableFloatingIP: to.Ptr(false), 299 LoadDistribution: to.Ptr(armnetwork.LoadDistributionDefault), 300 FrontendIPConfiguration: &armnetwork.SubResource{ 301 ID: to.Ptr(fmt.Sprintf("/%s/%s/frontendIPConfigurations/%s", in.idPrefix, in.loadBalancerName, existingFrontEndIPConfigName)), 302 }, 303 BackendAddressPool: &armnetwork.SubResource{ 304 ID: to.Ptr(fmt.Sprintf("/%s/%s/backendAddressPools/%s", in.idPrefix, in.loadBalancerName, in.backendAddressPoolName)), 305 }, 306 Probe: &armnetwork.SubResource{ 307 ID: to.Ptr(fmt.Sprintf("/%s/%s/probes/%s", in.idPrefix, in.loadBalancerName, mcsProbeName)), 308 }, 309 }, 310 } 311 312 intLB.Properties.Probes = append(intLB.Properties.Probes, mcsProbe) 313 intLB.Properties.LoadBalancingRules = append(intLB.Properties.LoadBalancingRules, mcsRule) 314 pollerResp, err := in.lbClient.BeginCreateOrUpdate(ctx, 315 in.resourceGroup, 316 in.loadBalancerName, 317 intLB, 318 nil) 319 if err != nil { 320 return nil, fmt.Errorf("cannot update load balancer: %w", err) 321 } 322 323 resp, err := pollerResp.PollUntilDone(ctx, nil) 324 if err != nil { 325 return nil, err 326 } 327 return &resp.LoadBalancer, nil 328 } 329 330 func associateVMToBackendPool(ctx context.Context, in vmInput) error { 331 for _, id := range in.ids { 332 vmName := path.Base(id) 333 vm, err := in.vmClient.Get(ctx, in.resourceGroup, vmName, nil) 334 if err != nil { 335 return fmt.Errorf("failed to get vm %s: %w", vmName, err) 336 } 337 338 if nics := vm.Properties.NetworkProfile.NetworkInterfaces; len(nics) == 1 { 339 nicRef := nics[0] 340 341 nicName := path.Base(*nicRef.ID) 342 nic, err := in.nicClient.Get(ctx, in.resourceGroup, nicName, nil) 343 if err != nil { 344 return fmt.Errorf("failed to get nic for vm %s: %w", vmName, err) 345 } 346 for _, ipconfig := range nic.Properties.IPConfigurations { 347 ipconfig.Properties.LoadBalancerBackendAddressPools = append(ipconfig.Properties.LoadBalancerBackendAddressPools, in.bap) 348 } 349 pollerResp, err := in.nicClient.BeginCreateOrUpdate(ctx, in.resourceGroup, nicName, nic.Interface, nil) 350 if err != nil { 351 return fmt.Errorf("failed to update nic for %s: %w", vmName, err) 352 } 353 _, err = pollerResp.PollUntilDone(ctx, nil) 354 if err != nil { 355 return fmt.Errorf("failed to update nic for vm %s: %w", vmName, err) 356 } 357 } else { 358 return fmt.Errorf("vm %s does not have a single nic: %w", vmName, err) 359 } 360 } 361 return nil 362 } 363 364 func addSecurityGroupRule(ctx context.Context, in *securityGroupInput) error { 365 securityRulesClient := in.networkClientFactory.NewSecurityRulesClient() 366 367 // Assume inbound tcp connections from any port to destination port for now 368 securityRuleResp, err := securityRulesClient.BeginCreateOrUpdate(ctx, 369 in.resourceGroupName, 370 in.securityGroupName, 371 in.securityRuleName, 372 armnetwork.SecurityRule{ 373 Name: ptr.To(in.securityRuleName), 374 Properties: &armnetwork.SecurityRulePropertiesFormat{ 375 Access: ptr.To(armnetwork.SecurityRuleAccessAllow), 376 Direction: ptr.To(armnetwork.SecurityRuleDirectionInbound), 377 Protocol: ptr.To(armnetwork.SecurityRuleProtocolTCP), 378 DestinationAddressPrefix: ptr.To("*"), 379 DestinationPortRange: ptr.To(in.securityRulePort), 380 Priority: ptr.To[int32](in.securityRulePriority), 381 SourceAddressPrefix: ptr.To("*"), 382 SourcePortRange: ptr.To("*"), 383 }, 384 }, 385 nil, 386 ) 387 if err != nil { 388 return fmt.Errorf("failed to add security rule: %w", err) 389 } 390 _, err = securityRuleResp.PollUntilDone(ctx, nil) 391 if err != nil { 392 return fmt.Errorf("failed to add security rule: %w", err) 393 } 394 395 return nil 396 } 397 398 func deleteSecurityGroupRule(ctx context.Context, in *securityGroupInput) error { 399 securityRulesClient := in.networkClientFactory.NewSecurityRulesClient() 400 securityRulesPoller, err := securityRulesClient.BeginDelete(ctx, in.resourceGroupName, in.securityGroupName, in.securityRuleName, nil) 401 if err != nil { 402 return fmt.Errorf("failed to delete security rule: %w", err) 403 } 404 _, err = securityRulesPoller.PollUntilDone(ctx, nil) 405 if err != nil { 406 return fmt.Errorf("failed to delete security rule: %w", err) 407 } 408 return nil 409 } 410 411 func addInboundNatRuleToLoadBalancer(ctx context.Context, in *inboundNatRuleInput) (*armnetwork.InboundNatRule, error) { 412 inboundNatRulesClient := in.networkClientFactory.NewInboundNatRulesClient() 413 inboundNatRulesPoller, err := inboundNatRulesClient.BeginCreateOrUpdate(ctx, 414 in.resourceGroupName, 415 in.loadBalancerName, 416 in.inboundNatRuleName, 417 armnetwork.InboundNatRule{ 418 Properties: &armnetwork.InboundNatRulePropertiesFormat{ 419 BackendPort: to.Ptr[int32](in.inboundNatRulePort), 420 FrontendIPConfiguration: &armnetwork.SubResource{ 421 ID: to.Ptr(in.frontendIPConfigID), 422 }, 423 FrontendPort: to.Ptr[int32](in.inboundNatRulePort), 424 Protocol: to.Ptr(armnetwork.TransportProtocolTCP), // assume TCP for now 425 }, 426 }, 427 nil, 428 ) 429 if err != nil { 430 return nil, fmt.Errorf("failed to add inbound nat rule to load balancer: %w", err) 431 } 432 inboundNatRuleResp, err := inboundNatRulesPoller.PollUntilDone(ctx, nil) 433 if err != nil { 434 return nil, fmt.Errorf("failed to add inbound nat rule to load balancer: %w", err) 435 } 436 437 return &inboundNatRuleResp.InboundNatRule, nil 438 } 439 440 func deleteInboundNatRule(ctx context.Context, in *inboundNatRuleInput) error { 441 inboundNatRulesClient := in.networkClientFactory.NewInboundNatRulesClient() 442 inboundNatRulesPoller, err := inboundNatRulesClient.BeginDelete(ctx, 443 in.resourceGroupName, 444 in.loadBalancerName, 445 in.inboundNatRuleName, 446 nil, 447 ) 448 if err != nil { 449 return fmt.Errorf("failed to delete inbound nat rule: %w", err) 450 } 451 _, err = inboundNatRulesPoller.PollUntilDone(ctx, nil) 452 if err != nil { 453 return fmt.Errorf("failed to delete inbound nat rule: %w", err) 454 } 455 return nil 456 } 457 458 func associateInboundNatRuleToInterface(ctx context.Context, in *inboundNatRuleInput) (*armnetwork.Interface, error) { 459 interfacesClient := in.networkClientFactory.NewInterfacesClient() 460 interfaceResp, err := interfacesClient.Get(ctx, 461 in.resourceGroupName, 462 in.bootstrapNicName, 463 nil, 464 ) 465 if err != nil { 466 return nil, fmt.Errorf("failed to get interface: %w", err) 467 } 468 bootstrapInterface := interfaceResp.Interface 469 470 inboundNatRulesClient := in.networkClientFactory.NewInboundNatRulesClient() 471 inboundNatRulesResp, err := inboundNatRulesClient.Get(ctx, 472 in.resourceGroupName, 473 in.loadBalancerName, 474 in.inboundNatRuleName, 475 nil, 476 ) 477 if err != nil { 478 return nil, fmt.Errorf("failed to get inbound nat rule: %w", err) 479 } 480 inboundNatRule := inboundNatRulesResp.InboundNatRule 481 482 for i, ipConfig := range bootstrapInterface.Properties.IPConfigurations { 483 ipConfig.Properties.LoadBalancerInboundNatRules = append(ipConfig.Properties.LoadBalancerInboundNatRules, 484 &inboundNatRule, 485 ) 486 bootstrapInterface.Properties.IPConfigurations[i] = ipConfig 487 } 488 489 interfacesPollerResp, err := interfacesClient.BeginCreateOrUpdate(ctx, 490 in.resourceGroupName, 491 in.bootstrapNicName, 492 bootstrapInterface, 493 nil, 494 ) 495 if err != nil { 496 return nil, fmt.Errorf("failed to add inbound nat rule to interface: %w", err) 497 } 498 499 interfacesResp, err := interfacesPollerResp.PollUntilDone(ctx, nil) 500 if err != nil { 501 return nil, fmt.Errorf("failed to add inbound nat rule to interface: %w", err) 502 } 503 return &interfacesResp.Interface, nil 504 }