Repository: kubernetes-sigs/cloud-provider-azure
Pull request #4553 (2023-09-04 04:46:10)

[release-1.27] feat: add support for disable tcp reset
This is an automated cherry-pick of #4519

/assign feiskyer

```release-note
LoadBalancer tcp reset could be disabled with Service annotation "service.beta.kubernetes.io/azure-load-balancer-disable-tcp-reset=true"
```


Patch:
diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go
index c095fef58..0f10b5f61 100644
--- a/pkg/consts/consts.go
+++ b/pkg/consts/consts.go
@@ -325,6 +325,9 @@ const (
 	// automatically on Azure LoadBalancer. Instead, they need to be configured manually (e.g. on Azure cross-region LoadBalancer by another operator).
 	ServiceAnnotationAdditionalPublicIPs = "service.beta.kubernetes.io/azure-additional-public-ips"
 
+	// ServiceAnnotationDisableTCPReset is the annotation used on the service to disable TCP reset on the load balancer.
+	ServiceAnnotationDisableTCPReset = "service.beta.kubernetes.io/azure-load-balancer-disable-tcp-reset"
+
 	// ServiceTagKey is the service key applied for public IP tags.
 	ServiceTagKey       = "k8s-azure-service"
 	LegacyServiceTagKey = "service"
diff --git a/pkg/consts/helpers.go b/pkg/consts/helpers.go
index b3fedf02c..591469764 100644
--- a/pkg/consts/helpers.go
+++ b/pkg/consts/helpers.go
@@ -65,6 +65,11 @@ func IsPLSEnabled(annotations map[string]string) bool {
 	return expectAttributeInSvcAnnotationBeEqualTo(annotations, ServiceAnnotationPLSCreation, TrueAnnotationValue)
 }
 
+// IsTCPResetDisabled return true if ServiceAnnotationDisableTCPReset is true
+func IsTCPResetDisabled(annotations map[string]string) bool {
+	return expectAttributeInSvcAnnotationBeEqualTo(annotations, ServiceAnnotationDisableTCPReset, TrueAnnotationValue)
+}
+
 // Getint32ValueFromK8sSvcAnnotation get health probe configuration for port
 func Getint32ValueFromK8sSvcAnnotation(annotations map[string]string, key string, validators ...Int32BusinessValidator) (*int32, error) {
 	val, err := GetAttributeValueInSvcAnnotation(annotations, key)
@@ -74,7 +79,7 @@ func Getint32ValueFromK8sSvcAnnotation(annotations map[string]string, key string
 	return nil, err
 }
 
-// BuildHealthProbeAnnotationKeyForPort get health probe configuration key for port
+// BuildAnnotationKeyForPort get health probe configuration key for port
 func BuildAnnotationKeyForPort(port int32, key PortParams) string {
 	return fmt.Sprintf(PortAnnotationPrefixPattern, port, string(key))
 }
diff --git a/pkg/provider/azure_loadbalancer.go b/pkg/provider/azure_loadbalancer.go
index 120043512..5bacbeb94 100644
--- a/pkg/provider/azure_loadbalancer.go
+++ b/pkg/provider/azure_loadbalancer.go
@@ -2400,7 +2400,7 @@ func (az *Cloud) getExpectedLoadBalancingRulePropertiesForPort(
 		IdleTimeoutInMinutes: lbIdleTimeout,
 	}
 	if strings.EqualFold(string(transportProto), string(network.TransportProtocolTCP)) && az.useStandardLoadBalancer() {
-		props.EnableTCPReset = pointer.Bool(true)
+		props.EnableTCPReset = pointer.Bool(!consts.IsTCPResetDisabled(service.Annotations))
 	}
 
 	// Azure ILB does not support secondary IPs as floating IPs on the LB. Therefore, floating IP needs to be turned
@@ -2421,7 +2421,8 @@ func (az *Cloud) getExpectedHAModeLoadBalancingRuleProperties(
 	if err != nil {
 		return nil, fmt.Errorf("error generate lb rule for ha mod loadbalancer. err: %w", err)
 	}
-	props.EnableTCPReset = pointer.Bool(true)
+	props.EnableTCPReset = pointer.Bool(!consts.IsTCPResetDisabled(service.Annotations))
+
 	return props, nil
 }
 


Test patch:
diff --git a/pkg/provider/azure_loadbalancer_test.go b/pkg/provider/azure_loadbalancer_test.go
index e8641543a..fb0db1c29 100644
--- a/pkg/provider/azure_loadbalancer_test.go
+++ b/pkg/provider/azure_loadbalancer_test.go
@@ -2623,6 +2623,15 @@ func TestReconcileLoadBalancerRuleCommon(t *testing.T) {
 			expectedRules:  getDefaultTestRules(false),
 			expectedProbes: getDefaultTestProbes("Http", "/"),
 		},
+		{
+			desc: "getExpectedLBRules should disable tcp reset when annotation is set",
+			service: getTestServiceDualStack("test1", v1.ProtocolTCP, map[string]string{
+				"service.beta.kubernetes.io/azure-load-balancer-disable-tcp-reset": "true",
+			}, 80),
+			loadBalancerSku: "standard",
+			expectedRules:   getTCPResetTestRules(false),
+			expectedProbes:  getDefaultTestProbes("Tcp", ""),
+		},
 		{
 			desc: "getExpectedLBRules should prioritize port specific probe protocol over appProtocol",
 			service: getTestServiceDualStack("test1", v1.ProtocolTCP, map[string]string{
@@ -2948,7 +2957,7 @@ func getDefaultTestRules(enableTCPReset bool) map[bool][]network.LoadBalancingRu
 
 // getDefaultInternalIPv6Rules returns a rule for IPv6 single stack.
 func getDefaultInternalIPv6Rules(enableTCPReset bool) map[bool][]network.LoadBalancingRule {
-	rule := getTestRule(true, 80, false)
+	rule := getTestRule(enableTCPReset, 80, false)
 	rule.EnableFloatingIP = pointer.Bool(false)
 	rule.BackendPort = pointer.Int32(getBackendPort(*rule.FrontendPort))
 	rule.BackendAddressPool.ID = pointer.String("backendPoolID-IPv6")
@@ -2957,6 +2966,18 @@ func getDefaultInternalIPv6Rules(enableTCPReset bool) map[bool][]network.LoadBal
 	}
 }
 
+// getTCPResetTestRules returns rules with TCPReset always set.
+func getTCPResetTestRules(enableTCPReset bool) map[bool][]network.LoadBalancingRule {
+	IPv4Rule := getTestRule(enableTCPReset, 80, consts.IPVersionIPv4)
+	IPv6Rule := getTestRule(enableTCPReset, 80, consts.IPVersionIPv6)
+	IPv4Rule.EnableTCPReset = pointer.Bool(enableTCPReset)
+	IPv6Rule.EnableTCPReset = pointer.Bool(enableTCPReset)
+	return map[bool][]network.LoadBalancingRule{
+		consts.IPVersionIPv4: {IPv4Rule},
+		consts.IPVersionIPv6: {IPv6Rule},
+	}
+}
+
 // getTestRule returns a rule for dualStack.
 func getTestRule(enableTCPReset bool, port int32, isIPv6 bool) network.LoadBalancingRule {
 	suffix := ""