Repository: getmoto/moto
Pull request #6818 (2023-09-15 19:58:35)

ELBv2: Improve Target Group validation
FYI @macnev2013 - some minor improvements to your earlier PRs.

The `healthcheck_timeout_seconds`-validation was tested against AWS - the additional attributes were deduced based on the docs and the TF tests.



Patch:
diff --git a/moto/elbv2/models.py b/moto/elbv2/models.py
index 69c1f13ac..81ea9740f 100644
--- a/moto/elbv2/models.py
+++ b/moto/elbv2/models.py
@@ -132,6 +132,12 @@ class FakeTargetGroup(CloudFormationModel):
             "slow_start.duration_seconds": 0,
             "waf.fail_open.enabled": "false",
         }
+        if target_type == "lambda":
+            self.attributes["lambda.multi_value_headers.enabled"] = "false"
+        if self.protocol in ["HTTP", "HTTPS"]:
+            self.attributes["stickiness.type"] = "lb_cookie"
+        if self.protocol in ["TCP", "UDP", "TCP_UDP"]:
+            self.attributes["stickiness.type"] = "source_ip"
 
         self.targets: Dict[str, Dict[str, Any]] = OrderedDict()
 
@@ -1201,6 +1207,7 @@ Member must satisfy regular expression pattern: {expression}"
                 {k: kwargs.get(k) or v for k, v in conditions["target_alb"].items()}
             )
 
+        original_kwargs = dict(kwargs)
         kwargs.update(kwargs_patch)
 
         healthcheck_timeout_seconds = int(
@@ -1219,7 +1226,14 @@ Member must satisfy regular expression pattern: {expression}"
                 raise ValidationError(
                     "Health check interval must be greater than the timeout."
                 )
-            if healthcheck_interval_seconds == healthcheck_timeout_seconds:
+            both_values_supplied = (
+                original_kwargs.get("healthcheck_timeout_seconds") is not None
+                and original_kwargs.get("healthcheck_interval_seconds") is not None
+            )
+            if (
+                both_values_supplied
+                and healthcheck_interval_seconds == healthcheck_timeout_seconds
+            ):
                 raise ValidationError(
                     f"Health check timeout '{healthcheck_timeout_seconds}' must be smaller than the interval '{healthcheck_interval_seconds}'"
                 )


Test patch:
diff --git a/tests/terraformtests/terraform-tests.success.txt b/tests/terraformtests/terraform-tests.success.txt
index b3c0f2f87..6a2020df2 100644
--- a/tests/terraformtests/terraform-tests.success.txt
+++ b/tests/terraformtests/terraform-tests.success.txt
@@ -269,6 +269,12 @@ elbv2:
   - TestAccELBV2ListenerCertificate
   - TestAccELBV2TargetGroupAttachment
   - TestAccELBV2TargetGroupDataSource
+  - TestAccELBV2TargetGroup_ALBAlias
+  - TestAccELBV2TargetGroup_networkLB
+  - TestAccELBV2TargetGroup_NetworkLB
+  - TestAccELBV2TargetGroup_Stickiness_defaultALB
+  - TestAccELBV2TargetGroup_Stickiness_valid
+  - TestAccELBV2TargetGroup_Stickiness_update
 events:
   - TestAccEventsAPIDestination
   - TestAccEventsArchive
diff --git a/tests/test_elbv2/test_elbv2_target_groups.py b/tests/test_elbv2/test_elbv2_target_groups.py
index 347c7f211..0bbce2355 100644
--- a/tests/test_elbv2/test_elbv2_target_groups.py
+++ b/tests/test_elbv2/test_elbv2_target_groups.py
@@ -380,9 +380,9 @@ def test_target_group_attributes():
     assert len(response["TargetGroups"]) == 1
     target_group_arn = target_group["TargetGroupArn"]
 
-    # The attributes should start with the two defaults
+    # The attributes should start with the defaults
     response = conn.describe_target_group_attributes(TargetGroupArn=target_group_arn)
-    assert len(response["Attributes"]) == 6
+    assert len(response["Attributes"]) == 7
     attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]}
     assert attributes["deregistration_delay.timeout_seconds"] == "300"
     assert attributes["stickiness.enabled"] == "false"
@@ -397,21 +397,21 @@ def test_target_group_attributes():
         TargetGroupArn=target_group_arn,
         Attributes=[
             {"Key": "stickiness.enabled", "Value": "true"},
-            {"Key": "stickiness.type", "Value": "lb_cookie"},
+            {"Key": "stickiness.type", "Value": "app_cookie"},
         ],
     )
 
     # The response should have only the keys updated
     assert len(response["Attributes"]) == 2
     attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]}
-    assert attributes["stickiness.type"] == "lb_cookie"
+    assert attributes["stickiness.type"] == "app_cookie"
     assert attributes["stickiness.enabled"] == "true"
 
     # These new values should be in the full attribute list
     response = conn.describe_target_group_attributes(TargetGroupArn=target_group_arn)
     assert len(response["Attributes"]) == 7
     attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]}
-    assert attributes["stickiness.type"] == "lb_cookie"
+    assert attributes["stickiness.type"] == "app_cookie"
     assert attributes["stickiness.enabled"] == "true"
 
 
@@ -828,9 +828,11 @@ def test_delete_target_group_while_listener_still_exists():
     client.delete_target_group(TargetGroupArn=target_group_arn1)
 
 
+@mock_ec2
 @mock_elbv2
 def test_create_target_group_validation_error():
     elbv2 = boto3.client("elbv2", region_name="us-east-1")
+    _, vpc, _, _, _, _ = create_load_balancer()
 
     with pytest.raises(ClientError) as ex:
         elbv2.create_target_group(
@@ -896,6 +898,43 @@ def test_create_target_group_validation_error():
     assert err["Code"] == "ValidationError"
     assert err["Message"] == "Health check interval must be greater than the timeout."
 
+    # When providing both values:
+    # Health check timeout '5' must be smaller than the interval '5'
+    #
+    # When only the Interval is supplied, it can be the same value as the default
+    group = elbv2.create_target_group(
+        Name="target1",
+        Port=443,
+        Protocol="TLS",
+        VpcId=vpc.id,
+        TargetType="ip",
+        IpAddressType="ipv6",
+        HealthCheckIntervalSeconds=10,
+        HealthCheckPort="traffic-port",
+        HealthCheckProtocol="TCP",
+        HealthyThresholdCount=3,
+        UnhealthyThresholdCount=3,
+    )["TargetGroups"][0]
+    assert group["HealthCheckIntervalSeconds"] == 10
+    assert group["HealthCheckTimeoutSeconds"] == 10
+
+    # Same idea goes the other way around
+    group = elbv2.create_target_group(
+        Name="target2",
+        Port=443,
+        Protocol="TLS",
+        VpcId=vpc.id,
+        TargetType="ip",
+        IpAddressType="ipv6",
+        HealthCheckTimeoutSeconds=30,
+        HealthCheckPort="traffic-port",
+        HealthCheckProtocol="TCP",
+        HealthyThresholdCount=3,
+        UnhealthyThresholdCount=3,
+    )["TargetGroups"][0]
+    assert group["HealthCheckIntervalSeconds"] == 30
+    assert group["HealthCheckTimeoutSeconds"] == 30
+
     with pytest.raises(ClientError) as ex:
         elbv2.create_target_group(Name="a-target", TargetType="lambda", Port=8080)
     err = ex.value.response["Error"]