sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/loadbalancers/spec_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 loadbalancers
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4"
    24  	. "github.com/onsi/gomega"
    25  	"k8s.io/utils/ptr"
    26  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    27  )
    28  
    29  func getExistingLBWithMissingFrontendIPConfigs() armnetwork.LoadBalancer {
    30  	existingLB := newSamplePublicAPIServerLB(false, true, true, true, true)
    31  	existingLB.Properties.FrontendIPConfigurations = []*armnetwork.FrontendIPConfiguration{}
    32  
    33  	return existingLB
    34  }
    35  
    36  func getExistingLBWithMissingBackendPool() armnetwork.LoadBalancer {
    37  	existingLB := newSamplePublicAPIServerLB(true, false, true, true, true)
    38  	existingLB.Properties.BackendAddressPools = []*armnetwork.BackendAddressPool{}
    39  
    40  	return existingLB
    41  }
    42  
    43  func getExistingLBWithMissingLBRules() armnetwork.LoadBalancer {
    44  	existingLB := newSamplePublicAPIServerLB(true, true, false, true, true)
    45  	existingLB.Properties.LoadBalancingRules = []*armnetwork.LoadBalancingRule{}
    46  
    47  	return existingLB
    48  }
    49  
    50  func getExistingLBWithMissingProbes() armnetwork.LoadBalancer {
    51  	existingLB := newSamplePublicAPIServerLB(true, true, true, false, true)
    52  	existingLB.Properties.Probes = []*armnetwork.Probe{}
    53  
    54  	return existingLB
    55  }
    56  
    57  func getExistingLBWithMissingOutboundRules() armnetwork.LoadBalancer {
    58  	existingLB := newSamplePublicAPIServerLB(true, true, true, true, false)
    59  	existingLB.Properties.OutboundRules = []*armnetwork.OutboundRule{}
    60  
    61  	return existingLB
    62  }
    63  
    64  func TestParameters(t *testing.T) {
    65  	testcases := []struct {
    66  		name          string
    67  		spec          *LBSpec
    68  		existing      interface{}
    69  		expect        func(g *WithT, result interface{})
    70  		expectedError string
    71  	}{
    72  		{
    73  			name:     "public API load balancer exists with all expected values",
    74  			spec:     &fakePublicAPILBSpec,
    75  			existing: newSamplePublicAPIServerLB(false, false, false, false, false),
    76  			expect: func(g *WithT, result interface{}) {
    77  				g.Expect(result).To(BeNil())
    78  			},
    79  			expectedError: "",
    80  		},
    81  		{
    82  			name:     "internal API load balancer with all expected values",
    83  			spec:     &fakeInternalAPILBSpec,
    84  			existing: newDefaultInternalAPIServerLB(),
    85  			expect: func(g *WithT, result interface{}) {
    86  				g.Expect(result).To(BeNil())
    87  			},
    88  			expectedError: "",
    89  		},
    90  		{
    91  			name:     "node outbound load balancer exists with all expected values",
    92  			spec:     &fakeNodeOutboundLBSpec,
    93  			existing: newDefaultNodeOutboundLB(),
    94  			expect: func(g *WithT, result interface{}) {
    95  				g.Expect(result).To(BeNil())
    96  			},
    97  			expectedError: "",
    98  		},
    99  		{
   100  			name:     "load balancer exists with missing frontend IP configs",
   101  			spec:     &fakePublicAPILBSpec,
   102  			existing: getExistingLBWithMissingFrontendIPConfigs(),
   103  			expect: func(g *WithT, result interface{}) {
   104  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
   105  				g.Expect(result.(armnetwork.LoadBalancer)).To(Equal(newSamplePublicAPIServerLB(false, true, true, true, true)))
   106  			},
   107  			expectedError: "",
   108  		},
   109  		{
   110  			name:     "load balancer exists with missing backend pool",
   111  			spec:     &fakePublicAPILBSpec,
   112  			existing: getExistingLBWithMissingBackendPool(),
   113  			expect: func(g *WithT, result interface{}) {
   114  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
   115  				g.Expect(result.(armnetwork.LoadBalancer)).To(Equal(newSamplePublicAPIServerLB(true, false, true, true, true)))
   116  			},
   117  			expectedError: "",
   118  		},
   119  		{
   120  			name:     "load balancer exists with missing load balancing rules",
   121  			spec:     &fakePublicAPILBSpec,
   122  			existing: getExistingLBWithMissingLBRules(),
   123  			expect: func(g *WithT, result interface{}) {
   124  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
   125  				g.Expect(result.(armnetwork.LoadBalancer)).To(Equal(newSamplePublicAPIServerLB(true, true, false, true, true)))
   126  			},
   127  			expectedError: "",
   128  		},
   129  		{
   130  			name:     "load balancer exists with missing probes",
   131  			spec:     &fakePublicAPILBSpec,
   132  			existing: getExistingLBWithMissingProbes(),
   133  			expect: func(g *WithT, result interface{}) {
   134  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
   135  				g.Expect(result.(armnetwork.LoadBalancer)).To(Equal(newSamplePublicAPIServerLB(true, true, true, false, true)))
   136  			},
   137  			expectedError: "",
   138  		},
   139  		{
   140  			name:     "load balancer exists with missing outbound rules",
   141  			spec:     &fakePublicAPILBSpec,
   142  			existing: getExistingLBWithMissingOutboundRules(),
   143  			expect: func(g *WithT, result interface{}) {
   144  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
   145  				g.Expect(result.(armnetwork.LoadBalancer)).To(Equal(newSamplePublicAPIServerLB(true, true, true, true, false)))
   146  			},
   147  			expectedError: "",
   148  		},
   149  	}
   150  	for _, tc := range testcases {
   151  		tc := tc
   152  		t.Run(tc.name, func(t *testing.T) {
   153  			g := NewWithT(t)
   154  			t.Parallel()
   155  
   156  			result, err := tc.spec.Parameters(context.TODO(), tc.existing)
   157  			if tc.expectedError != "" {
   158  				g.Expect(err).To(HaveOccurred())
   159  				g.Expect(err).To(MatchError(tc.expectedError))
   160  			} else {
   161  				g.Expect(err).NotTo(HaveOccurred())
   162  			}
   163  			tc.expect(g, result)
   164  		})
   165  	}
   166  }
   167  
   168  func newDefaultNodeOutboundLB() armnetwork.LoadBalancer {
   169  	return armnetwork.LoadBalancer{
   170  		Tags: map[string]*string{
   171  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   172  			"sigs.k8s.io_cluster-api-provider-azure_role":               ptr.To(infrav1.NodeOutboundRole),
   173  		},
   174  		SKU:      &armnetwork.LoadBalancerSKU{Name: ptr.To(armnetwork.LoadBalancerSKUNameStandard)},
   175  		Location: ptr.To("my-location"),
   176  		Properties: &armnetwork.LoadBalancerPropertiesFormat{
   177  			FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
   178  				{
   179  					Name: ptr.To("my-cluster-frontEnd"),
   180  					Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
   181  						PublicIPAddress: &armnetwork.PublicIPAddress{ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/publicIPAddresses/outbound-publicip")},
   182  					},
   183  				},
   184  			},
   185  			BackendAddressPools: []*armnetwork.BackendAddressPool{
   186  				{
   187  					Name: ptr.To("my-cluster-outboundBackendPool"),
   188  				},
   189  			},
   190  			LoadBalancingRules: []*armnetwork.LoadBalancingRule{},
   191  			Probes:             []*armnetwork.Probe{},
   192  			OutboundRules: []*armnetwork.OutboundRule{
   193  				{
   194  					Name: ptr.To("OutboundNATAllProtocols"),
   195  					Properties: &armnetwork.OutboundRulePropertiesFormat{
   196  						FrontendIPConfigurations: []*armnetwork.SubResource{
   197  							{ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-cluster/frontendIPConfigurations/my-cluster-frontEnd")},
   198  						},
   199  						BackendAddressPool: &armnetwork.SubResource{
   200  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-cluster/backendAddressPools/my-cluster-outboundBackendPool"),
   201  						},
   202  						Protocol:             ptr.To(armnetwork.LoadBalancerOutboundRuleProtocolAll),
   203  						IdleTimeoutInMinutes: ptr.To[int32](30),
   204  					},
   205  				},
   206  			},
   207  		},
   208  	}
   209  }
   210  
   211  func newSamplePublicAPIServerLB(verifyFrontendIP bool, verifyBackendAddressPools bool, verifyLBRules bool, verifyProbes bool, verifyOutboundRules bool) armnetwork.LoadBalancer {
   212  	var subnet *armnetwork.Subnet
   213  	var backendAddressPoolProps *armnetwork.BackendAddressPoolPropertiesFormat
   214  	enableFloatingIP := ptr.To(false)
   215  	numProbes := ptr.To[int32](4)
   216  	idleTimeout := ptr.To[int32](4)
   217  
   218  	if verifyFrontendIP {
   219  		subnet = &armnetwork.Subnet{
   220  			Name: ptr.To("fake-test-subnet"),
   221  		}
   222  	}
   223  	if verifyBackendAddressPools {
   224  		backendAddressPoolProps = &armnetwork.BackendAddressPoolPropertiesFormat{
   225  			Location: ptr.To("fake-test-location"),
   226  		}
   227  	}
   228  	if verifyLBRules {
   229  		enableFloatingIP = ptr.To(true)
   230  	}
   231  	if verifyProbes {
   232  		numProbes = ptr.To[int32](999)
   233  	}
   234  	if verifyOutboundRules {
   235  		idleTimeout = ptr.To[int32](1000)
   236  	}
   237  
   238  	return armnetwork.LoadBalancer{
   239  		Tags: map[string]*string{
   240  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   241  			"sigs.k8s.io_cluster-api-provider-azure_role":               ptr.To(infrav1.APIServerRole),
   242  		},
   243  		SKU:      &armnetwork.LoadBalancerSKU{Name: ptr.To(armnetwork.LoadBalancerSKUNameStandard)},
   244  		Location: ptr.To("my-location"),
   245  		Properties: &armnetwork.LoadBalancerPropertiesFormat{
   246  			FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
   247  				{
   248  					Name: ptr.To("my-publiclb-frontEnd"),
   249  					Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
   250  						PublicIPAddress: &armnetwork.PublicIPAddress{ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/publicIPAddresses/my-publicip")},
   251  						Subnet:          subnet, // Add to verify that FrontendIPConfigurations aren't overwritten on update
   252  					},
   253  				},
   254  			},
   255  			BackendAddressPools: []*armnetwork.BackendAddressPool{
   256  				{
   257  					Name:       ptr.To("my-publiclb-backendPool"),
   258  					Properties: backendAddressPoolProps, // Add to verify that BackendAddressPools aren't overwritten on update
   259  				},
   260  			},
   261  			LoadBalancingRules: []*armnetwork.LoadBalancingRule{
   262  				{
   263  					Name: ptr.To(lbRuleHTTPS),
   264  					Properties: &armnetwork.LoadBalancingRulePropertiesFormat{
   265  						DisableOutboundSnat:  ptr.To(true),
   266  						Protocol:             ptr.To(armnetwork.TransportProtocolTCP),
   267  						FrontendPort:         ptr.To[int32](6443),
   268  						BackendPort:          ptr.To[int32](6443),
   269  						IdleTimeoutInMinutes: ptr.To[int32](4),
   270  						EnableFloatingIP:     enableFloatingIP, // Add to verify that LoadBalancingRules aren't overwritten on update
   271  						LoadDistribution:     ptr.To(armnetwork.LoadDistributionDefault),
   272  						FrontendIPConfiguration: &armnetwork.SubResource{
   273  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-publiclb/frontendIPConfigurations/my-publiclb-frontEnd"),
   274  						},
   275  						BackendAddressPool: &armnetwork.SubResource{
   276  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-publiclb/backendAddressPools/my-publiclb-backendPool"),
   277  						},
   278  						Probe: &armnetwork.SubResource{
   279  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-publiclb/probes/HTTPSProbe"),
   280  						},
   281  					},
   282  				},
   283  			},
   284  			Probes: []*armnetwork.Probe{
   285  				{
   286  					Name: ptr.To(httpsProbe),
   287  					Properties: &armnetwork.ProbePropertiesFormat{
   288  						Protocol:          ptr.To(armnetwork.ProbeProtocolHTTPS),
   289  						Port:              ptr.To[int32](6443),
   290  						RequestPath:       ptr.To(httpsProbeRequestPath),
   291  						IntervalInSeconds: ptr.To[int32](15),
   292  						NumberOfProbes:    numProbes, // Add to verify that Probes aren't overwritten on update
   293  					},
   294  				},
   295  			},
   296  			OutboundRules: []*armnetwork.OutboundRule{
   297  				{
   298  					Name: ptr.To("OutboundNATAllProtocols"),
   299  					Properties: &armnetwork.OutboundRulePropertiesFormat{
   300  						FrontendIPConfigurations: []*armnetwork.SubResource{
   301  							{ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-publiclb/frontendIPConfigurations/my-publiclb-frontEnd")},
   302  						},
   303  						BackendAddressPool: &armnetwork.SubResource{
   304  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-publiclb/backendAddressPools/my-publiclb-backendPool"),
   305  						},
   306  						Protocol:             ptr.To(armnetwork.LoadBalancerOutboundRuleProtocolAll),
   307  						IdleTimeoutInMinutes: idleTimeout, // Add to verify that OutboundRules aren't overwritten on update
   308  					},
   309  				},
   310  			},
   311  		},
   312  	}
   313  }
   314  
   315  func newDefaultInternalAPIServerLB() armnetwork.LoadBalancer {
   316  	return armnetwork.LoadBalancer{
   317  		Tags: map[string]*string{
   318  			"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   319  			"sigs.k8s.io_cluster-api-provider-azure_role":               ptr.To(infrav1.APIServerRole),
   320  		},
   321  		SKU:      &armnetwork.LoadBalancerSKU{Name: ptr.To(armnetwork.LoadBalancerSKUNameStandard)},
   322  		Location: ptr.To("my-location"),
   323  		Properties: &armnetwork.LoadBalancerPropertiesFormat{
   324  			FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
   325  				{
   326  					Name: ptr.To("my-private-lb-frontEnd"),
   327  					Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
   328  						PrivateIPAllocationMethod: ptr.To(armnetwork.IPAllocationMethodStatic),
   329  						Subnet: &armnetwork.Subnet{
   330  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-cp-subnet"),
   331  						},
   332  						PrivateIPAddress: ptr.To("10.0.0.10"),
   333  					},
   334  				},
   335  			},
   336  			BackendAddressPools: []*armnetwork.BackendAddressPool{
   337  				{
   338  					Name: ptr.To("my-private-lb-backendPool"),
   339  				},
   340  			},
   341  			LoadBalancingRules: []*armnetwork.LoadBalancingRule{
   342  				{
   343  					Name: ptr.To(lbRuleHTTPS),
   344  					Properties: &armnetwork.LoadBalancingRulePropertiesFormat{
   345  						DisableOutboundSnat:  ptr.To(true),
   346  						Protocol:             ptr.To(armnetwork.TransportProtocolTCP),
   347  						FrontendPort:         ptr.To[int32](6443),
   348  						BackendPort:          ptr.To[int32](6443),
   349  						IdleTimeoutInMinutes: ptr.To[int32](4),
   350  						EnableFloatingIP:     ptr.To(false),
   351  						LoadDistribution:     ptr.To(armnetwork.LoadDistributionDefault),
   352  						FrontendIPConfiguration: &armnetwork.SubResource{
   353  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-private-lb/frontendIPConfigurations/my-private-lb-frontEnd"),
   354  						},
   355  						BackendAddressPool: &armnetwork.SubResource{
   356  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-private-lb/backendAddressPools/my-private-lb-backendPool"),
   357  						},
   358  						Probe: &armnetwork.SubResource{
   359  							ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-private-lb/probes/HTTPSProbe"),
   360  						},
   361  					},
   362  				},
   363  			},
   364  			OutboundRules: []*armnetwork.OutboundRule{},
   365  			Probes: []*armnetwork.Probe{
   366  				{
   367  					Name: ptr.To(httpsProbe),
   368  					Properties: &armnetwork.ProbePropertiesFormat{
   369  						Protocol:          ptr.To(armnetwork.ProbeProtocolHTTPS),
   370  						Port:              ptr.To[int32](6443),
   371  						RequestPath:       ptr.To(httpsProbeRequestPath),
   372  						IntervalInSeconds: ptr.To[int32](15),
   373  						NumberOfProbes:    ptr.To[int32](4),
   374  					},
   375  				},
   376  			},
   377  		},
   378  	}
   379  }