Repository: kubernetes-sigs/cloud-provider-azure
Pull request #4555 (2023-09-04 05:03:00)

[release-1.25] 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 fe7ce854e..65ac4bca2 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 291af7446..c18f127ab 100644
--- a/pkg/consts/helpers.go
+++ b/pkg/consts/helpers.go
@@ -70,6 +70,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)
@@ -79,7 +84,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 e9212ccab..2913ae064 100644
--- a/pkg/provider/azure_loadbalancer.go
+++ b/pkg/provider/azure_loadbalancer.go
@@ -2314,7 +2314,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
@@ -2335,7 +2335,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 8325c614d..aa6f2f1c3 100644
--- a/pkg/provider/azure_loadbalancer_test.go
+++ b/pkg/provider/azure_loadbalancer_test.go
@@ -2471,6 +2471,15 @@ func TestReconcileLoadBalancerRule(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: getTestService("test1", v1.ProtocolTCP, map[string]string{
@@ -2776,7 +2785,7 @@ func getDefaultTestRules(enableTCPReset bool) map[bool][]network.LoadBalancingRu
 }
 
 func getDefaultInternalIPv6Rules(enableTCPReset bool) map[bool][]network.LoadBalancingRule {
-	rulesDualStack := getDefaultTestRules(true)
+	rulesDualStack := getDefaultTestRules(enableTCPReset)
 	for _, rules := range rulesDualStack {
 		for _, rule := range rules {
 			rule.EnableFloatingIP = pointer.Bool(false)
@@ -2786,6 +2795,18 @@ func getDefaultInternalIPv6Rules(enableTCPReset bool) map[bool][]network.LoadBal
 	return rulesDualStack
 }
 
+// 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},
+	}
+}
+
 func getTestRule(enableTCPReset bool, port int32, isIPv6 bool) network.LoadBalancingRule {
 	suffix := ""
 	if isIPv6 {