ELBv2: target-group creation and modification improvements (#6886)

This commit is contained in:
Macwan Nevil 2023-10-12 02:46:58 +05:30 committed by GitHub
parent d4b8e07be8
commit b1acc97e36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 350 additions and 12 deletions

View File

@ -187,3 +187,8 @@ class InvalidLoadBalancerActionException(ELBClientError):
class ValidationError(ELBClientError): class ValidationError(ELBClientError):
def __init__(self, msg: str): def __init__(self, msg: str):
super().__init__("ValidationError", msg) super().__init__("ValidationError", msg)
class InvalidConfigurationRequest(ELBClientError):
def __init__(self, msg: str):
super().__init__("InvalidConfigurationRequest", msg)

View File

@ -36,6 +36,7 @@ from .exceptions import (
InvalidStatusCodeActionTypeError, InvalidStatusCodeActionTypeError,
InvalidLoadBalancerActionException, InvalidLoadBalancerActionException,
ValidationError, ValidationError,
InvalidConfigurationRequest,
) )
ALLOWED_ACTIONS = [ ALLOWED_ACTIONS = [
@ -1257,6 +1258,72 @@ Member must satisfy regular expression pattern: {expression}"
if not target_group: if not target_group:
raise TargetGroupNotFoundError() raise TargetGroupNotFoundError()
deregistration_delay_timeout_seconds = attributes.get(
"deregistration_delay.timeout_seconds"
)
if deregistration_delay_timeout_seconds:
if int(deregistration_delay_timeout_seconds) not in range(0, 3600):
raise ValidationError(
f"'deregistration_delay.timeout_seconds' value '{deregistration_delay_timeout_seconds}' must be between '0-3600' inclusive"
)
if target_group.target_type == "lambda":
raise InvalidConfigurationRequest(
"A target group with target type 'lambda' does not support the attribute deregistration_delay.timeout_seconds"
)
stickiness_type = attributes.get("stickiness.type")
# TODO: strict type checking for app_cookie
stickiness_cookie_name = attributes.get("stickiness.app_cookie.cookie_name")
if stickiness_type:
if target_group.protocol == "GENEVE":
if stickiness_cookie_name:
# TODO: generalise error message
raise ValidationError(
"Target group attribute key 'stickiness.app_cookie.cookie_name' is not recognized"
)
elif stickiness_type not in [
"source_ip_dest_ip_proto",
"source_ip_dest_ip",
]:
raise ValidationError(
f"'{stickiness_type}' must be one of [source_ip_dest_ip_proto, source_ip_dest_ip]"
)
if stickiness_type == "source_ip":
if target_group.protocol in ["HTTP", "HTTPS"]:
raise InvalidConfigurationRequest(
f"Stickiness type 'source_ip' is not supported for target groups with the {target_group.protocol} protocol"
)
elif target_group.protocol == "TLS":
raise InvalidConfigurationRequest(
"You cannot enable stickiness on target groups with the TLS protocol"
)
elif stickiness_type == "lb_cookie":
if target_group.protocol in ["TCP", "TLS", "UDP", "TCP_UDP"]:
raise InvalidConfigurationRequest(
f"Stickiness type 'lb_cookie' is not supported for target groups with the {target_group.protocol} protocol"
)
elif stickiness_type == "app_cookie":
if not stickiness_cookie_name:
raise InvalidConfigurationRequest(
"You must set an application cookie name to enable stickiness of type 'app_cookie'"
)
if target_group.protocol in ["TCP", "TLS", "UDP", "TCP_UDP", "GENEVE"]:
raise InvalidConfigurationRequest(
f"Stickiness type 'app_cookie' is not supported for target groups with the {target_group.protocol} protocol"
)
elif stickiness_type in ["source_ip_dest_ip", "source_ip_dest_ip_proto"]:
if target_group.protocol in [
"HTTP",
"HTTPS",
"TCP",
"TLS",
"UDP",
"TCP_UDP",
]:
raise ValidationError(
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]"
)
target_group.attributes.update(attributes) target_group.attributes.update(attributes)
def convert_and_validate_certificates( def convert_and_validate_certificates(

View File

@ -844,7 +844,8 @@ CREATE_TARGET_GROUP_TEMPLATE = """<CreateTargetGroupResponse xmlns="http://elast
<HealthCheckEnabled>{{ target_group.healthcheck_enabled and 'true' or 'false' }}</HealthCheckEnabled> <HealthCheckEnabled>{{ target_group.healthcheck_enabled and 'true' or 'false' }}</HealthCheckEnabled>
{% if target_group.matcher %} {% if target_group.matcher %}
<Matcher> <Matcher>
<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode> {% if target_group.matcher.get("HttpCode") %}<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode>{% endif %}
{% if target_group.matcher.get("GrpcCode") %}<GrpcCode>{{ target_group.matcher['GrpcCode'] }}</GrpcCode>{% endif %}
</Matcher> </Matcher>
{% endif %} {% endif %}
{% if target_group.target_type %} {% if target_group.target_type %}
@ -1098,7 +1099,8 @@ DESCRIBE_TARGET_GROUPS_TEMPLATE = """<DescribeTargetGroupsResponse xmlns="http:/
<UnhealthyThresholdCount>{{ target_group.unhealthy_threshold_count }}</UnhealthyThresholdCount> <UnhealthyThresholdCount>{{ target_group.unhealthy_threshold_count }}</UnhealthyThresholdCount>
{% if target_group.matcher %} {% if target_group.matcher %}
<Matcher> <Matcher>
<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode> {% if target_group.matcher.get("HttpCode") %}<HttpCode>{{ target_group.matcher['HttpCode'] }}</HttpCode>{% endif %}
{% if target_group.matcher.get("GrpcCode") %}<GrpcCode>{{ target_group.matcher['GrpcCode'] }}</GrpcCode>{% endif %}
</Matcher> </Matcher>
{% endif %} {% endif %}
{% if target_group.target_type %} {% if target_group.target_type %}

View File

@ -19,5 +19,9 @@ PATCH="etc/0001-Patch-Hardcode-endpoints-to-local-server.patch"
( (
cd terraform-provider-aws || exit cd terraform-provider-aws || exit
echo "Running tests $2 for service $1..." echo "Running tests $2 for service $1..."
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test TF_ACC=true go test ./internal/service/$1/ -v -timeout 60m -run $2 TF_ACC=1 \
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test \
AWS_ALTERNATE_ACCESS_KEY_ID=test AWS_ALTERNATE_SECRET_ACCESS_KEY=test \
AWS_THIRD_SECRET_ACCESS_KEY=test AWS_THIRD_ACCESS_KEY_ID=test \
go test ./internal/service/$1/ -v -timeout 60m -run $2
) )

View File

@ -57,5 +57,8 @@ TestAccLambdaFunctionURL_Cors
TestAccVPCNATGateway_privateIP TestAccVPCNATGateway_privateIP
TestAccVPCSecurityGroupRule_multiDescription TestAccVPCSecurityGroupRule_multiDescription
TestAccELBV2TargetGroup_ProtocolVersion_basic
TestAccELBV2TargetGroup_ForceNew_port
TestAccELBV2TargetGroup_ForceNew_protocol
# TF expects a wrong error message, which is not in-line with what AWS returns # TF expects a wrong error message, which is not in-line with what AWS returns
TestAccEFSFileSystemDataSource_nonExistent_fileSystemID TestAccEFSFileSystemDataSource_nonExistent_fileSystemID

View File

@ -267,14 +267,58 @@ elb:
- TestAccELBSSLNegotiationPolicy - TestAccELBSSLNegotiationPolicy
elbv2: elbv2:
- TestAccELBV2ListenerCertificate - TestAccELBV2ListenerCertificate
- TestAccELBV2TargetGroupAttachment - TestAccELBV2TargetGroup_backwardsCompatibility
- TestAccELBV2TargetGroupDataSource - TestAccELBV2TargetGroup_ProtocolVersion_grpcHealthCheck
- TestAccELBV2TargetGroup_ALBAlias - TestAccELBV2TargetGroup_ProtocolVersion_grpcUpdate
- TestAccELBV2TargetGroup_networkLB - TestAccELBV2TargetGroup_ipAddressType
- TestAccELBV2TargetGroup_NetworkLB - TestAccELBV2TargetGroup_tls
- TestAccELBV2TargetGroup_HealthCheck_tcpHTTPS
- TestAccELBV2TargetGroup_attrsOnCreate
- TestAccELBV2TargetGroup_basic
- TestAccELBV2TargetGroup_udp
- TestAccELBV2TargetGroup_ForceNew_name
- TestAccELBV2TargetGroup_ForceNew_vpc
- TestAccELBV2TargetGroup_Defaults_application
- TestAccELBV2TargetGroup_Defaults_network
- TestAccELBV2TargetGroup_HealthCheck_enable
- TestAccELBV2TargetGroup_Name_generated
- TestAccELBV2TargetGroup_Name_prefix
- TestAccELBV2TargetGroup_NetworkLB_tcpHealthCheckUpdated
- TestAccELBV2TargetGroup_networkLB_TargetGroupWithConnectionTermination
- TestAccELBV2TargetGroup_NetworkLB_targetGroupWithProxy
- TestAccELBV2TargetGroup_preserveClientIPValid
- TestAccELBV2TargetGroup_Geneve_basic
- TestAccELBV2TargetGroup_Geneve_notSticky
- TestAccELBV2TargetGroup_Geneve_Sticky
- TestAccELBV2TargetGroup_Geneve_targetFailover
- TestAccELBV2TargetGroup_Stickiness_defaultALB - TestAccELBV2TargetGroup_Stickiness_defaultALB
- TestAccELBV2TargetGroup_Stickiness_valid - TestAccELBV2TargetGroup_Stickiness_defaultNLB
- TestAccELBV2TargetGroup_Stickiness_update - TestAccELBV2TargetGroup_Stickiness_invalidALB
- TestAccELBV2TargetGroup_Stickiness_invalidNLB
- TestAccELBV2TargetGroup_Stickiness_validALB
- TestAccELBV2TargetGroup_Stickiness_validNLB
- TestAccELBV2TargetGroup_tags
- TestAccELBV2TargetGroup_Stickiness_updateAppEnabled
- TestAccELBV2TargetGroup_HealthCheck_update
- TestAccELBV2TargetGroup_Stickiness_updateEnabled
- TestAccELBV2TargetGroup_HealthCheck_without
- TestAccELBV2TargetGroup_ALBAlias_basic
- TestAccELBV2TargetGroup_ALBAlias_changeNameForceNew
- TestAccELBV2TargetGroup_ALBAlias_changePortForceNew
- TestAccELBV2TargetGroup_ALBAlias_changeProtocolForceNew
- TestAccELBV2TargetGroup_ALBAlias_changeVPCForceNew
- TestAccELBV2TargetGroup_ALBAlias_generatedName
- TestAccELBV2TargetGroup_ALBAlias_lambda
- TestAccELBV2TargetGroup_ALBAlias_lambdaMultiValueHeadersEnabled
- TestAccELBV2TargetGroup_ALBAlias_missingPortProtocolVPC
- TestAccELBV2TargetGroup_ALBAlias_namePrefix
- TestAccELBV2TargetGroup_ALBAlias_setAndUpdateSlowStart
- TestAccELBV2TargetGroup_ALBAlias_tags
- TestAccELBV2TargetGroup_ALBAlias_updateHealthCheck
- TestAccELBV2TargetGroup_ALBAlias_updateLoadBalancingAlgorithmType
- TestAccELBV2TargetGroup_ALBAlias_updateLoadBalancingCrossZoneEnabled
- TestAccELBV2TargetGroup_ALBAlias_updateStickinessEnabled
- TestAccELBV2TargetGroup_Name_noDuplicates
events: events:
- TestAccEventsAPIDestination - TestAccEventsAPIDestination
- TestAccEventsArchive - TestAccEventsArchive

View File

@ -398,21 +398,24 @@ def test_target_group_attributes():
Attributes=[ Attributes=[
{"Key": "stickiness.enabled", "Value": "true"}, {"Key": "stickiness.enabled", "Value": "true"},
{"Key": "stickiness.type", "Value": "app_cookie"}, {"Key": "stickiness.type", "Value": "app_cookie"},
{"Key": "stickiness.app_cookie.cookie_name", "Value": "my_cookie"},
], ],
) )
# The response should have only the keys updated # The response should have only the keys updated
assert len(response["Attributes"]) == 2 assert len(response["Attributes"]) == 3
attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]} attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]}
assert attributes["stickiness.type"] == "app_cookie" assert attributes["stickiness.type"] == "app_cookie"
assert attributes["stickiness.enabled"] == "true" assert attributes["stickiness.enabled"] == "true"
assert attributes["stickiness.app_cookie.cookie_name"] == "my_cookie"
# These new values should be in the full attribute list # These new values should be in the full attribute list
response = conn.describe_target_group_attributes(TargetGroupArn=target_group_arn) response = conn.describe_target_group_attributes(TargetGroupArn=target_group_arn)
assert len(response["Attributes"]) == 7 assert len(response["Attributes"]) == 8
attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]} attributes = {attr["Key"]: attr["Value"] for attr in response["Attributes"]}
assert attributes["stickiness.type"] == "app_cookie" assert attributes["stickiness.type"] == "app_cookie"
assert attributes["stickiness.enabled"] == "true" assert attributes["stickiness.enabled"] == "true"
assert attributes["stickiness.app_cookie.cookie_name"] == "my_cookie"
@mock_elbv2 @mock_elbv2
@ -993,3 +996,213 @@ def test_create_target_group_healthcheck_validation(protocol_name, should_raise)
assert exc.value.response["Error"]["Message"] == _get_error_message( assert exc.value.response["Error"]["Message"] == _get_error_message(
protocol_name, 5, 5 protocol_name, 5, 5
) )
@mock_ec2
@mock_elbv2
@pytest.mark.parametrize(
"protocol, should_raise, stickiness_type, error_message",
[
# stickiness.type = "source_ip"
(
"HTTP",
True,
"source_ip",
"Stickiness type 'source_ip' is not supported for target groups with the HTTP protocol",
),
(
"HTTPS",
True,
"source_ip",
"Stickiness type 'source_ip' is not supported for target groups with the HTTPS protocol",
),
("TCP", False, "source_ip", ""),
(
"TLS",
True,
"source_ip",
"You cannot enable stickiness on target groups with the TLS protocol",
),
("UDP", False, "source_ip", ""),
("TCP_UDP", False, "source_ip", ""),
(
"GENEVE",
True,
"source_ip",
"'source_ip' must be one of [source_ip_dest_ip_proto, source_ip_dest_ip]",
),
# stickiness.type = "lb_cookie"
("HTTP", False, "lb_cookie", ""),
("HTTPS", False, "lb_cookie", ""),
(
"TCP",
True,
"lb_cookie",
"Stickiness type 'lb_cookie' is not supported for target groups with the TCP protocol",
),
(
"TLS",
True,
"lb_cookie",
"Stickiness type 'lb_cookie' is not supported for target groups with the TLS protocol",
),
(
"UDP",
True,
"lb_cookie",
"Stickiness type 'lb_cookie' is not supported for target groups with the UDP protocol",
),
(
"TCP_UDP",
True,
"lb_cookie",
"Stickiness type 'lb_cookie' is not supported for target groups with the TCP_UDP protocol",
),
(
"GENEVE",
True,
"lb_cookie",
"'lb_cookie' must be one of [source_ip_dest_ip_proto, source_ip_dest_ip]",
),
# stickiness.type = "app_cookie"
("HTTP", False, "app_cookie", ""),
("HTTPS", False, "app_cookie", ""),
(
"TCP",
True,
"app_cookie",
"Stickiness type 'app_cookie' is not supported for target groups with the TCP protocol",
),
(
"TLS",
True,
"app_cookie",
"Stickiness type 'app_cookie' is not supported for target groups with the TLS protocol",
),
(
"UDP",
True,
"app_cookie",
"Stickiness type 'app_cookie' is not supported for target groups with the UDP protocol",
),
(
"TCP_UDP",
True,
"app_cookie",
"Stickiness type 'app_cookie' is not supported for target groups with the TCP_UDP protocol",
),
(
"GENEVE",
True,
"app_cookie",
"Target group attribute key 'stickiness.app_cookie.cookie_name' is not recognized",
),
# stickiness.type = "source_ip_dest_ip"
(
"HTTP",
True,
"source_ip_dest_ip",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"HTTPS",
True,
"source_ip_dest_ip",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"TCP",
True,
"source_ip_dest_ip",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"TLS",
True,
"source_ip_dest_ip",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"UDP",
True,
"source_ip_dest_ip",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"TCP_UDP",
True,
"source_ip_dest_ip",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
("GENEVE", False, "source_ip_dest_ip", ""),
# stickiness.type = "source_ip_dest_ip_proto"
(
"HTTPS",
True,
"source_ip_dest_ip_proto",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"HTTP",
True,
"source_ip_dest_ip_proto",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"TCP",
True,
"source_ip_dest_ip_proto",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"TLS",
True,
"source_ip_dest_ip_proto",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"UDP",
True,
"source_ip_dest_ip_proto",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
(
"TCP_UDP",
True,
"source_ip_dest_ip_proto",
"'Stickiness type' must be one of [app_cookie, lb_cookie, source_ip]",
),
("GENEVE", False, "source_ip_dest_ip_proto", ""),
],
)
def test_target_group_attributes_stickiness(
protocol, should_raise, stickiness_type, error_message
):
elbv2 = boto3.client("elbv2", region_name="us-east-1")
_, vpc, _, _, _, _ = create_load_balancer()
response = elbv2.create_target_group(
Name="a-target",
Protocol=protocol,
Port=6081 if protocol == "GENEVE" else 80,
VpcId=vpc.id,
)
target_group_arn = response["TargetGroups"][0]["TargetGroupArn"]
attributes = [
{"Key": "stickiness.enabled", "Value": "true"},
{"Key": "stickiness.type", "Value": stickiness_type},
]
if stickiness_type == "app_cookie":
attributes.append(
{"Key": "stickiness.app_cookie.cookie_name", "Value": "localstack"}
)
if should_raise:
with pytest.raises(ClientError) as exc:
elbv2.modify_target_group_attributes(
TargetGroupArn=target_group_arn, Attributes=attributes
)
assert exc.value.response["Error"]["Message"] == error_message
else:
elbv2.modify_target_group_attributes(
TargetGroupArn=target_group_arn, Attributes=attributes
)