ELBv2 improvements (#4808)
This commit is contained in:
parent
e1dbec1dff
commit
ed86df6bae
@ -62,6 +62,7 @@ class FakeTargetGroup(CloudFormationModel):
|
|||||||
vpc_id,
|
vpc_id,
|
||||||
protocol,
|
protocol,
|
||||||
port,
|
port,
|
||||||
|
protocol_version=None,
|
||||||
healthcheck_protocol=None,
|
healthcheck_protocol=None,
|
||||||
healthcheck_port=None,
|
healthcheck_port=None,
|
||||||
healthcheck_path=None,
|
healthcheck_path=None,
|
||||||
@ -79,6 +80,7 @@ class FakeTargetGroup(CloudFormationModel):
|
|||||||
self.arn = arn
|
self.arn = arn
|
||||||
self.vpc_id = vpc_id
|
self.vpc_id = vpc_id
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
|
self.protocol_version = protocol_version or "HTTP1"
|
||||||
self.port = port
|
self.port = port
|
||||||
self.healthcheck_protocol = healthcheck_protocol or self.protocol
|
self.healthcheck_protocol = healthcheck_protocol or self.protocol
|
||||||
self.healthcheck_port = healthcheck_port
|
self.healthcheck_port = healthcheck_port
|
||||||
@ -912,7 +914,7 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
if target_group.name == name:
|
if target_group.name == name:
|
||||||
raise DuplicateTargetGroupName()
|
raise DuplicateTargetGroupName()
|
||||||
|
|
||||||
valid_protocols = ["HTTPS", "HTTP", "TCP"]
|
valid_protocols = ["HTTPS", "HTTP", "TCP", "TLS", "UDP", "TCP_UDP", "GENEVE"]
|
||||||
if (
|
if (
|
||||||
kwargs.get("healthcheck_protocol")
|
kwargs.get("healthcheck_protocol")
|
||||||
and kwargs["healthcheck_protocol"] not in valid_protocols
|
and kwargs["healthcheck_protocol"] not in valid_protocols
|
||||||
@ -948,6 +950,13 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
self.target_groups[target_group.arn] = target_group
|
self.target_groups[target_group.arn] = target_group
|
||||||
return target_group
|
return target_group
|
||||||
|
|
||||||
|
def modify_target_group_attributes(self, target_group_arn, attributes):
|
||||||
|
target_group = self.target_groups.get(target_group_arn)
|
||||||
|
if not target_group:
|
||||||
|
raise TargetGroupNotFoundError()
|
||||||
|
|
||||||
|
target_group.attributes.update(attributes)
|
||||||
|
|
||||||
def convert_and_validate_certificates(self, certificates):
|
def convert_and_validate_certificates(self, certificates):
|
||||||
|
|
||||||
# transform default certificate to conform with the rest of the code and XML templates
|
# transform default certificate to conform with the rest of the code and XML templates
|
||||||
@ -1355,7 +1364,7 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
"HttpCode must be like 200 | 200-399 | 200,201 ...",
|
"HttpCode must be like 200 | 200-399 | 200,201 ...",
|
||||||
)
|
)
|
||||||
|
|
||||||
if http_codes is not None:
|
if http_codes is not None and target_group.protocol in ["HTTP", "HTTPS"]:
|
||||||
target_group.matcher["HttpCode"] = http_codes
|
target_group.matcher["HttpCode"] = http_codes
|
||||||
if health_check_interval is not None:
|
if health_check_interval is not None:
|
||||||
target_group.healthcheck_interval_seconds = health_check_interval
|
target_group.healthcheck_interval_seconds = health_check_interval
|
||||||
@ -1397,34 +1406,35 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
if port is not None:
|
if port is not None:
|
||||||
listener.port = port
|
listener.port = port
|
||||||
|
|
||||||
if protocol is not None:
|
if protocol not in (None, "HTTP", "HTTPS", "TCP"):
|
||||||
if protocol not in ("HTTP", "HTTPS", "TCP"):
|
raise RESTError(
|
||||||
|
"UnsupportedProtocol", "Protocol {0} is not supported".format(protocol),
|
||||||
|
)
|
||||||
|
|
||||||
|
# HTTPS checks
|
||||||
|
protocol_becomes_https = protocol == "HTTPS"
|
||||||
|
protocol_stays_https = protocol is None and listener.protocol == "HTTPS"
|
||||||
|
if protocol_becomes_https or protocol_stays_https:
|
||||||
|
# Check certificates exist
|
||||||
|
if certificates:
|
||||||
|
default_cert = certificates[0]
|
||||||
|
default_cert_arn = default_cert["certificate_arn"]
|
||||||
|
try:
|
||||||
|
self.acm_backend.get_certificate(default_cert_arn)
|
||||||
|
except Exception:
|
||||||
|
raise RESTError(
|
||||||
|
"CertificateNotFound",
|
||||||
|
"Certificate {0} not found".format(default_cert_arn),
|
||||||
|
)
|
||||||
|
listener.certificate = default_cert_arn
|
||||||
|
listener.certificates = certificates
|
||||||
|
else:
|
||||||
raise RESTError(
|
raise RESTError(
|
||||||
"UnsupportedProtocol",
|
"CertificateWereNotPassed",
|
||||||
"Protocol {0} is not supported".format(protocol),
|
"You must provide a list containing exactly one certificate if the listener protocol is HTTPS.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# HTTPS checks
|
if protocol is not None:
|
||||||
if protocol == "HTTPS":
|
|
||||||
# Check certificates exist
|
|
||||||
if certificates:
|
|
||||||
default_cert = certificates[0]
|
|
||||||
default_cert_arn = default_cert["certificate_arn"]
|
|
||||||
try:
|
|
||||||
self.acm_backend.get_certificate(default_cert_arn)
|
|
||||||
except Exception:
|
|
||||||
raise RESTError(
|
|
||||||
"CertificateNotFound",
|
|
||||||
"Certificate {0} not found".format(default_cert_arn),
|
|
||||||
)
|
|
||||||
listener.certificate = default_cert_arn
|
|
||||||
listener.certificates = certificates
|
|
||||||
else:
|
|
||||||
raise RESTError(
|
|
||||||
"CertificateWereNotPassed",
|
|
||||||
"You must provide a list containing exactly one certificate if the listener protocol is HTTPS.",
|
|
||||||
)
|
|
||||||
|
|
||||||
listener.protocol = protocol
|
listener.protocol = protocol
|
||||||
|
|
||||||
if ssl_policy is not None:
|
if ssl_policy is not None:
|
||||||
|
@ -178,6 +178,7 @@ class ELBV2Response(BaseResponse):
|
|||||||
name = self._get_param("Name")
|
name = self._get_param("Name")
|
||||||
vpc_id = self._get_param("VpcId")
|
vpc_id = self._get_param("VpcId")
|
||||||
protocol = self._get_param("Protocol")
|
protocol = self._get_param("Protocol")
|
||||||
|
protocol_version = self._get_param("ProtocolVersion", "HTTP1")
|
||||||
port = self._get_param("Port")
|
port = self._get_param("Port")
|
||||||
healthcheck_protocol = self._get_param("HealthCheckProtocol")
|
healthcheck_protocol = self._get_param("HealthCheckProtocol")
|
||||||
healthcheck_port = self._get_param("HealthCheckPort")
|
healthcheck_port = self._get_param("HealthCheckPort")
|
||||||
@ -194,6 +195,7 @@ class ELBV2Response(BaseResponse):
|
|||||||
name,
|
name,
|
||||||
vpc_id=vpc_id,
|
vpc_id=vpc_id,
|
||||||
protocol=protocol,
|
protocol=protocol,
|
||||||
|
protocol_version=protocol_version,
|
||||||
port=port,
|
port=port,
|
||||||
healthcheck_protocol=healthcheck_protocol,
|
healthcheck_protocol=healthcheck_protocol,
|
||||||
healthcheck_port=healthcheck_port,
|
healthcheck_port=healthcheck_port,
|
||||||
@ -366,14 +368,10 @@ class ELBV2Response(BaseResponse):
|
|||||||
@amzn_request_id
|
@amzn_request_id
|
||||||
def modify_target_group_attributes(self):
|
def modify_target_group_attributes(self):
|
||||||
target_group_arn = self._get_param("TargetGroupArn")
|
target_group_arn = self._get_param("TargetGroupArn")
|
||||||
target_group = self.elbv2_backend.target_groups.get(target_group_arn)
|
attributes = self._get_list_prefix("Attributes.member")
|
||||||
attributes = {
|
attributes = {attr["key"]: attr["value"] for attr in attributes}
|
||||||
attr["key"]: attr["value"]
|
self.elbv2_backend.modify_target_group_attributes(target_group_arn, attributes)
|
||||||
for attr in self._get_list_prefix("Attributes.member")
|
|
||||||
}
|
|
||||||
target_group.attributes.update(attributes)
|
|
||||||
if not target_group:
|
|
||||||
raise TargetGroupNotFoundError()
|
|
||||||
template = self.response_template(MODIFY_TARGET_GROUP_ATTRIBUTES_TEMPLATE)
|
template = self.response_template(MODIFY_TARGET_GROUP_ATTRIBUTES_TEMPLATE)
|
||||||
return template.render(attributes=attributes)
|
return template.render(attributes=attributes)
|
||||||
|
|
||||||
@ -1085,6 +1083,7 @@ DESCRIBE_TARGET_GROUPS_TEMPLATE = """<DescribeTargetGroupsResponse xmlns="http:/
|
|||||||
<TargetGroupName>{{ target_group.name }}</TargetGroupName>
|
<TargetGroupName>{{ target_group.name }}</TargetGroupName>
|
||||||
{% if target_group.protocol %}
|
{% if target_group.protocol %}
|
||||||
<Protocol>{{ target_group.protocol }}</Protocol>
|
<Protocol>{{ target_group.protocol }}</Protocol>
|
||||||
|
<ProtocolVersion>{{ target_group.protocol_version }}</ProtocolVersion>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if target_group.port %}
|
{% if target_group.port %}
|
||||||
<Port>{{ target_group.port }}</Port>
|
<Port>{{ target_group.port }}</Port>
|
||||||
@ -1608,6 +1607,7 @@ DESCRIBE_LOADBALANCER_ATTRS_TEMPLATE = """<DescribeLoadBalancerAttributesRespons
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</DescribeLoadBalancerAttributesResponse>"""
|
</DescribeLoadBalancerAttributesResponse>"""
|
||||||
|
|
||||||
|
|
||||||
MODIFY_TARGET_GROUP_TEMPLATE = """<ModifyTargetGroupResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
MODIFY_TARGET_GROUP_TEMPLATE = """<ModifyTargetGroupResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||||
<ModifyTargetGroupResult>
|
<ModifyTargetGroupResult>
|
||||||
<TargetGroups>
|
<TargetGroups>
|
||||||
@ -1650,7 +1650,7 @@ MODIFY_LISTENER_TEMPLATE = """<ModifyListenerResponse xmlns="http://elasticloadb
|
|||||||
<Certificates>
|
<Certificates>
|
||||||
{% for cert in listener.certificates %}
|
{% for cert in listener.certificates %}
|
||||||
<member>
|
<member>
|
||||||
<CertificateArn>{{ cert }}</CertificateArn>
|
<CertificateArn>{{ cert["certificate_arn"] }}</CertificateArn>
|
||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</Certificates>
|
</Certificates>
|
||||||
|
@ -1354,7 +1354,7 @@ def test_modify_listener_http_to_https():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Bad cert
|
# Bad cert
|
||||||
with pytest.raises(ClientError):
|
with pytest.raises(ClientError) as exc:
|
||||||
client.modify_listener(
|
client.modify_listener(
|
||||||
ListenerArn=listener_arn,
|
ListenerArn=listener_arn,
|
||||||
Port=443,
|
Port=443,
|
||||||
@ -1363,6 +1363,85 @@ def test_modify_listener_http_to_https():
|
|||||||
Certificates=[{"CertificateArn": "lalala", "IsDefault": True}],
|
Certificates=[{"CertificateArn": "lalala", "IsDefault": True}],
|
||||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||||
)
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Message"].should.equal("Certificate lalala not found")
|
||||||
|
|
||||||
|
# Unknown protocol
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.modify_listener(
|
||||||
|
ListenerArn=listener_arn,
|
||||||
|
Port=443,
|
||||||
|
Protocol="HTP",
|
||||||
|
SslPolicy="ELBSecurityPolicy-TLS-1-2-2017-01",
|
||||||
|
Certificates=[{"CertificateArn": yahoo_arn, "IsDefault": True}],
|
||||||
|
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Message"].should.equal("Protocol HTP is not supported")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_acm
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
def test_modify_listener_of_https_target_group():
|
||||||
|
# Verify we can add a listener for a TargetGroup that is already HTTPS
|
||||||
|
client = boto3.client("elbv2", region_name="eu-central-1")
|
||||||
|
acm = boto3.client("acm", region_name="eu-central-1")
|
||||||
|
ec2 = boto3.resource("ec2", region_name="eu-central-1")
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="a-security-group", Description="First One"
|
||||||
|
)
|
||||||
|
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||||
|
subnet1 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone="eu-central-1a"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.create_load_balancer(
|
||||||
|
Name="my-lb",
|
||||||
|
Subnets=[subnet1.id],
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
Scheme="internal",
|
||||||
|
Tags=[{"Key": "key_name", "Value": "a_value"}],
|
||||||
|
)
|
||||||
|
|
||||||
|
load_balancer_arn = response.get("LoadBalancers")[0].get("LoadBalancerArn")
|
||||||
|
|
||||||
|
response = client.create_target_group(
|
||||||
|
Name="a-target", Protocol="HTTPS", Port=8443, VpcId=vpc.id,
|
||||||
|
)
|
||||||
|
target_group = response.get("TargetGroups")[0]
|
||||||
|
target_group_arn = target_group["TargetGroupArn"]
|
||||||
|
|
||||||
|
# HTTPS listener
|
||||||
|
response = acm.request_certificate(
|
||||||
|
DomainName="google.com", SubjectAlternativeNames=["google.com"],
|
||||||
|
)
|
||||||
|
google_arn = response["CertificateArn"]
|
||||||
|
response = client.create_listener(
|
||||||
|
LoadBalancerArn=load_balancer_arn,
|
||||||
|
Protocol="HTTPS",
|
||||||
|
Port=443,
|
||||||
|
Certificates=[{"CertificateArn": google_arn}],
|
||||||
|
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||||
|
)
|
||||||
|
listener_arn = response["Listeners"][0]["ListenerArn"]
|
||||||
|
|
||||||
|
# Now modify the HTTPS listener with a different certificate
|
||||||
|
response = acm.request_certificate(
|
||||||
|
DomainName="yahoo.com", SubjectAlternativeNames=["yahoo.com"],
|
||||||
|
)
|
||||||
|
yahoo_arn = response["CertificateArn"]
|
||||||
|
|
||||||
|
listener = client.modify_listener(
|
||||||
|
ListenerArn=listener_arn,
|
||||||
|
Certificates=[{"CertificateArn": yahoo_arn,},],
|
||||||
|
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||||
|
)["Listeners"][0]
|
||||||
|
listener["Certificates"].should.equal([{"CertificateArn": yahoo_arn}])
|
||||||
|
|
||||||
|
listener = client.describe_listeners(ListenerArns=[listener_arn])["Listeners"][0]
|
||||||
|
listener["Certificates"].should.equal([{"CertificateArn": yahoo_arn}])
|
||||||
|
|
||||||
|
|
||||||
@mock_elbv2
|
@mock_elbv2
|
||||||
|
@ -32,7 +32,7 @@ def test_create_target_group_with_invalid_healthcheck_protocol():
|
|||||||
err = exc.value.response["Error"]
|
err = exc.value.response["Error"]
|
||||||
err["Code"].should.equal("ValidationError")
|
err["Code"].should.equal("ValidationError")
|
||||||
err["Message"].should.equal(
|
err["Message"].should.equal(
|
||||||
"Value /HTTP at 'healthCheckProtocol' failed to satisfy constraint: Member must satisfy enum value set: ['HTTPS', 'HTTP', 'TCP']"
|
"Value /HTTP at 'healthCheckProtocol' failed to satisfy constraint: Member must satisfy enum value set: ['HTTPS', 'HTTP', 'TCP', 'TLS', 'UDP', 'TCP_UDP', 'GENEVE']"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -499,6 +499,8 @@ def test_modify_target_group():
|
|||||||
response["TargetGroups"][0]["HealthCheckProtocol"].should.equal("HTTPS")
|
response["TargetGroups"][0]["HealthCheckProtocol"].should.equal("HTTPS")
|
||||||
response["TargetGroups"][0]["HealthCheckTimeoutSeconds"].should.equal(10)
|
response["TargetGroups"][0]["HealthCheckTimeoutSeconds"].should.equal(10)
|
||||||
response["TargetGroups"][0]["HealthyThresholdCount"].should.equal(10)
|
response["TargetGroups"][0]["HealthyThresholdCount"].should.equal(10)
|
||||||
|
response["TargetGroups"][0].should.have.key("Protocol").equals("HTTP")
|
||||||
|
response["TargetGroups"][0].should.have.key("ProtocolVersion").equals("HTTP1")
|
||||||
response["TargetGroups"][0]["UnhealthyThresholdCount"].should.equal(4)
|
response["TargetGroups"][0]["UnhealthyThresholdCount"].should.equal(4)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user