Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-1.0] fix: use zones in the pre-existing frontend IP configurations for int… #1095

Merged
merged 1 commit into from
Jan 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 38 additions & 9 deletions pkg/provider/azure_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1718,16 +1718,21 @@ func (az *Cloud) reconcileFrontendIPConfigs(clusterName string, service *v1.Serv
}
}
} else {
var (
previousZone *[]string
isFipChanged bool
)
for i := len(newConfigs) - 1; i >= 0; i-- {
config := newConfigs[i]
isFipChanged, err := az.isFrontendIPChanged(clusterName, config, service, defaultLBFrontendIPConfigName)
isFipChanged, err = az.isFrontendIPChanged(clusterName, config, service, defaultLBFrontendIPConfigName)
if err != nil {
return nil, false, err
}
if isFipChanged {
klog.V(2).Infof("reconcileLoadBalancer for service (%s)(%t): lb frontendconfig(%s) - dropping", serviceName, wantLb, *config.Name)
newConfigs = append(newConfigs[:i], newConfigs[i+1:]...)
dirtyConfigs = true
previousZone = config.Zones
}
}

Expand Down Expand Up @@ -1794,14 +1799,11 @@ func (az *Cloud) reconcileFrontendIPConfigs(clusterName string, service *v1.Serv
FrontendIPConfigurationPropertiesFormat: fipConfigurationProperties,
}

// only add zone information for new internal frontend IP configurations for standard load balancer not deployed to an edge zone.
location := az.Location
zones, err := az.getRegionZonesBackoff(location)
if err != nil {
return nil, false, err
}
if isInternal && az.useStandardLoadBalancer() && len(zones) > 0 && !az.HasExtendedLocation() {
newConfig.Zones = &zones
if isInternal {
if err := az.getFrontendZones(&newConfig, previousZone, isFipChanged, serviceName, defaultLBFrontendIPConfigName); err != nil {
klog.Errorf("reconcileLoadBalancer for service (%s)(%t): failed to getFrontendZones: %s", serviceName, wantLb, err.Error())
return nil, false, err
}
}
newConfigs = append(newConfigs, newConfig)
klog.V(2).Infof("reconcileLoadBalancer for service (%s)(%t): lb frontendconfig(%s) - adding", serviceName, wantLb, defaultLBFrontendIPConfigName)
Expand All @@ -1816,6 +1818,33 @@ func (az *Cloud) reconcileFrontendIPConfigs(clusterName string, service *v1.Serv
return ownedFIPConfig, dirtyConfigs, err
}

func (az *Cloud) getFrontendZones(
fipConfig *network.FrontendIPConfiguration,
previousZone *[]string,
isFipChanged bool,
serviceName, defaultLBFrontendIPConfigName string) error {
if !isFipChanged { // fetch zone information from API for new frontends
// only add zone information for new internal frontend IP configurations for standard load balancer not deployed to an edge zone.
location := az.Location
zones, err := az.getRegionZonesBackoff(location)
if err != nil {
return err
}
if az.useStandardLoadBalancer() && len(zones) > 0 && !az.HasExtendedLocation() {
fipConfig.Zones = &zones
}
} else {
if previousZone == nil { // keep the existing zone information for existing frontends
klog.V(2).Infof("getFrontendZones for service (%s): lb frontendconfig(%s): setting zone to nil", serviceName, defaultLBFrontendIPConfigName)
} else {
zoneStr := strings.Join(*previousZone, ",")
klog.V(2).Infof("getFrontendZones for service (%s): lb frontendconfig(%s): setting zone to %s", serviceName, defaultLBFrontendIPConfigName, zoneStr)
}
fipConfig.Zones = previousZone
}
return nil
}

func (az *Cloud) reconcileBackendPools(clusterName string, service *v1.Service, nodes []*v1.Node, lb *network.LoadBalancer, isBackendPoolPreConfigured bool) (bool, bool, error) {
var newBackendPools []network.BackendAddressPool
var err error
Expand Down
55 changes: 54 additions & 1 deletion pkg/provider/azure_loadbalancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/subnetclient/mocksubnetclient"
"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/vmclient/mockvmclient"
"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/vmssclient/mockvmssclient"
"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/zoneclient/mockzoneclient"
"sigs.k8s.io/cloud-provider-azure/pkg/consts"
"sigs.k8s.io/cloud-provider-azure/pkg/retry"
)
Expand Down Expand Up @@ -4347,9 +4348,11 @@ func TestReconcileZonesForFrontendIPConfigs(t *testing.T) {
existingFrontendIPConfigs []network.FrontendIPConfiguration
existingPIP network.PublicIPAddress
getPIPError *retry.Error
getZoneError *retry.Error
regionZonesMap map[string][]string
expectedZones *[]string
expectedDirty bool
expectedErr error
}{
{
description: "reconcileFrontendIPConfigs should reconcile the zones for the new fip config",
Expand All @@ -4370,6 +4373,48 @@ func TestReconcileZonesForFrontendIPConfigs(t *testing.T) {
expectedZones: &[]string{"1", "2", "3"},
expectedDirty: true,
},
{
description: "reconcileFrontendIPConfigs should report an error if failed to get zones",
service: getInternalTestService("test", 80),
getZoneError: retry.NewError(false, errors.New("get zone failed")),
expectedErr: errors.New("get zone failed"),
},
{
description: "reconcileFrontendIPConfigs should use the nil zones of the existing frontend",
service: getTestServiceWithAnnotation("test", map[string]string{
consts.ServiceAnnotationLoadBalancerInternalSubnet: "subnet",
consts.ServiceAnnotationLoadBalancerInternal: consts.TrueAnnotationValue}, 80),
existingFrontendIPConfigs: []network.FrontendIPConfiguration{
{
Name: to.StringPtr("atest1"),
FrontendIPConfigurationPropertiesFormat: &network.FrontendIPConfigurationPropertiesFormat{
Subnet: &network.Subnet{
Name: to.StringPtr("subnet-1"),
},
},
},
},
expectedDirty: true,
},
{
description: "reconcileFrontendIPConfigs should use the non-nil zones of the existing frontend",
service: getTestServiceWithAnnotation("test", map[string]string{
consts.ServiceAnnotationLoadBalancerInternalSubnet: "subnet",
consts.ServiceAnnotationLoadBalancerInternal: consts.TrueAnnotationValue}, 80),
existingFrontendIPConfigs: []network.FrontendIPConfiguration{
{
Name: to.StringPtr("atest1"),
FrontendIPConfigurationPropertiesFormat: &network.FrontendIPConfigurationPropertiesFormat{
Subnet: &network.Subnet{
Name: to.StringPtr("subnet-1"),
},
},
Zones: &[]string{"1"},
},
},
expectedZones: &[]string{"1"},
expectedDirty: true,
},
} {
t.Run(tc.description, func(t *testing.T) {
cloud := GetTestCloud(ctrl)
Expand All @@ -4387,9 +4432,17 @@ func TestReconcileZonesForFrontendIPConfigs(t *testing.T) {
subnetClient := cloud.SubnetsClient.(*mocksubnetclient.MockInterface)
subnetClient.EXPECT().Get(gomock.Any(), "rg", "vnet", "subnet", gomock.Any()).Return(network.Subnet{}, nil).MaxTimes(1)

zoneClient := mockzoneclient.NewMockInterface(ctrl)
zoneClient.EXPECT().GetZones(gomock.Any(), gomock.Any()).Return(map[string][]string{}, tc.getZoneError).MaxTimes(1)
cloud.ZoneClient = zoneClient

defaultLBFrontendIPConfigName := cloud.getDefaultFrontendIPConfigName(&tc.service)
_, dirty, err := cloud.reconcileFrontendIPConfigs("testCluster", &tc.service, &lb, true, defaultLBFrontendIPConfigName)
assert.NoError(t, err)
if tc.expectedErr == nil {
assert.NoError(t, err)
} else {
assert.Contains(t, err.Error(), tc.expectedErr.Error())
}
assert.Equal(t, tc.expectedDirty, dirty)

for _, fip := range *lb.FrontendIPConfigurations {
Expand Down
7 changes: 6 additions & 1 deletion pkg/provider/azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1530,8 +1530,13 @@ func getTestService(identifier string, proto v1.Protocol, annotations map[string
}

func getInternalTestService(identifier string, requestedPorts ...int32) v1.Service {
return getTestServiceWithAnnotation(identifier, map[string]string{consts.ServiceAnnotationLoadBalancerInternal: consts.TrueAnnotationValue}, requestedPorts...)
}
func getTestServiceWithAnnotation(identifier string, annotations map[string]string, requestedPorts ...int32) v1.Service {
svc := getTestService(identifier, v1.ProtocolTCP, nil, false, requestedPorts...)
svc.Annotations[consts.ServiceAnnotationLoadBalancerInternal] = consts.TrueAnnotationValue
for k, v := range annotations {
svc.Annotations[k] = v
}
return svc
}

Expand Down