Add support for AWS::ElasticLoadBalancingV2::ListenerRule (#3969)
* Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
This commit is contained in:
parent
a281f9d4a6
commit
b82096ba37
@ -1,4 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import random
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
@ -214,8 +215,9 @@ class FakeListener(CloudFormationModel):
|
|||||||
self.certificate = certificate
|
self.certificate = certificate
|
||||||
self.certificates = [certificate] if certificate is not None else []
|
self.certificates = [certificate] if certificate is not None else []
|
||||||
self.default_actions = default_actions
|
self.default_actions = default_actions
|
||||||
self._non_default_rules = []
|
self._non_default_rules = OrderedDict()
|
||||||
self._default_rule = FakeRule(
|
self._default_rule = OrderedDict()
|
||||||
|
self._default_rule[0] = FakeRule(
|
||||||
listener_arn=self.arn,
|
listener_arn=self.arn,
|
||||||
conditions=[],
|
conditions=[],
|
||||||
priority="default",
|
priority="default",
|
||||||
@ -229,17 +231,17 @@ class FakeListener(CloudFormationModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def rules(self):
|
def rules(self):
|
||||||
return self._non_default_rules + [self._default_rule]
|
return OrderedDict(
|
||||||
|
list(self._non_default_rules.items()) + list(self._default_rule.items())
|
||||||
def remove_rule(self, rule):
|
|
||||||
self._non_default_rules.remove(rule)
|
|
||||||
|
|
||||||
def register(self, rule):
|
|
||||||
self._non_default_rules.append(rule)
|
|
||||||
self._non_default_rules = sorted(
|
|
||||||
self._non_default_rules, key=lambda x: x.priority
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def remove_rule(self, arn):
|
||||||
|
self._non_default_rules.pop(arn)
|
||||||
|
|
||||||
|
def register(self, arn, rule):
|
||||||
|
self._non_default_rules[arn] = rule
|
||||||
|
sorted(self._non_default_rules.values(), key=lambda x: x.priority)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cloudformation_name_type():
|
def cloudformation_name_type():
|
||||||
return None
|
return None
|
||||||
@ -262,16 +264,12 @@ class FakeListener(CloudFormationModel):
|
|||||||
ssl_policy = properties.get("SslPolicy")
|
ssl_policy = properties.get("SslPolicy")
|
||||||
certificates = properties.get("Certificates")
|
certificates = properties.get("Certificates")
|
||||||
# transform default actions to confirm with the rest of the code and XML templates
|
# transform default actions to confirm with the rest of the code and XML templates
|
||||||
if "DefaultActions" in properties:
|
|
||||||
default_actions = []
|
default_actions = []
|
||||||
for i, action in enumerate(properties["DefaultActions"]):
|
for i, action in enumerate(properties["DefaultActions"]):
|
||||||
action_type = action["Type"]
|
action_type = action["Type"]
|
||||||
if action_type == "forward":
|
if action_type == "forward":
|
||||||
default_actions.append(
|
default_actions.append(
|
||||||
{
|
{"type": action_type, "target_group_arn": action["TargetGroupArn"],}
|
||||||
"type": action_type,
|
|
||||||
"target_group_arn": action["TargetGroupArn"],
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
elif action_type in [
|
elif action_type in [
|
||||||
"redirect",
|
"redirect",
|
||||||
@ -280,14 +278,10 @@ class FakeListener(CloudFormationModel):
|
|||||||
]:
|
]:
|
||||||
redirect_action = {"type": action_type}
|
redirect_action = {"type": action_type}
|
||||||
key = (
|
key = (
|
||||||
underscores_to_camelcase(
|
underscores_to_camelcase(action_type.capitalize().replace("-", "_"))
|
||||||
action_type.capitalize().replace("-", "_")
|
|
||||||
)
|
|
||||||
+ "Config"
|
+ "Config"
|
||||||
)
|
)
|
||||||
for redirect_config_key, redirect_config_value in action[
|
for redirect_config_key, redirect_config_value in action[key].items():
|
||||||
key
|
|
||||||
].items():
|
|
||||||
# need to match the output of _get_list_prefix
|
# need to match the output of _get_list_prefix
|
||||||
redirect_action[
|
redirect_action[
|
||||||
camelcase_to_underscores(key)
|
camelcase_to_underscores(key)
|
||||||
@ -297,8 +291,6 @@ class FakeListener(CloudFormationModel):
|
|||||||
default_actions.append(redirect_action)
|
default_actions.append(redirect_action)
|
||||||
else:
|
else:
|
||||||
raise InvalidActionTypeError(action_type, i + 1)
|
raise InvalidActionTypeError(action_type, i + 1)
|
||||||
else:
|
|
||||||
default_actions = None
|
|
||||||
|
|
||||||
listener = elbv2_backend.create_listener(
|
listener = elbv2_backend.create_listener(
|
||||||
load_balancer_arn, protocol, port, ssl_policy, certificates, default_actions
|
load_balancer_arn, protocol, port, ssl_policy, certificates, default_actions
|
||||||
@ -306,6 +298,81 @@ class FakeListener(CloudFormationModel):
|
|||||||
return listener
|
return listener
|
||||||
|
|
||||||
|
|
||||||
|
class FakeListenerRule(CloudFormationModel):
|
||||||
|
def __init__(
|
||||||
|
self, listener_arn, arn, conditions, priority, actions,
|
||||||
|
):
|
||||||
|
self.listener_arn = listener_arn
|
||||||
|
self.arn = arn
|
||||||
|
self.conditions = conditions
|
||||||
|
self.actions = actions
|
||||||
|
self.priority = priority
|
||||||
|
|
||||||
|
@property
|
||||||
|
def physical_resource_id(self):
|
||||||
|
return self.arn
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cloudformation_type():
|
||||||
|
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listenerrule.html
|
||||||
|
return "AWS::ElasticLoadBalancingV2::ListenerRule"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_from_cloudformation_json(
|
||||||
|
cls, resource_name, cloudformation_json, region_name
|
||||||
|
):
|
||||||
|
properties = cloudformation_json["Properties"]
|
||||||
|
elbv2_backend = elbv2_backends[region_name]
|
||||||
|
listener_arn = properties.get("ListenerArn")
|
||||||
|
priority = properties.get("Priority")
|
||||||
|
conditions = properties.get("Conditions")
|
||||||
|
# transform Actions to confirm with the rest of the code and XML templates
|
||||||
|
default_actions = []
|
||||||
|
for i, action in enumerate(properties["Actions"]):
|
||||||
|
action_type = action["Type"]
|
||||||
|
if action_type == "forward":
|
||||||
|
default_actions.append(
|
||||||
|
{"type": action_type, "target_group_arn": action["TargetGroupArn"],}
|
||||||
|
)
|
||||||
|
elif action_type in [
|
||||||
|
"redirect",
|
||||||
|
"authenticate-cognito",
|
||||||
|
"fixed-response",
|
||||||
|
]:
|
||||||
|
redirect_action = {"type": action_type}
|
||||||
|
key = (
|
||||||
|
underscores_to_camelcase(action_type.capitalize().replace("-", "_"))
|
||||||
|
+ "Config"
|
||||||
|
)
|
||||||
|
for redirect_config_key, redirect_config_value in action[key].items():
|
||||||
|
# need to match the output of _get_list_prefix
|
||||||
|
redirect_action[
|
||||||
|
camelcase_to_underscores(key)
|
||||||
|
+ "._"
|
||||||
|
+ camelcase_to_underscores(redirect_config_key)
|
||||||
|
] = redirect_config_value
|
||||||
|
default_actions.append(redirect_action)
|
||||||
|
else:
|
||||||
|
raise InvalidActionTypeError(action_type, i + 1)
|
||||||
|
|
||||||
|
listener_rule = elbv2_backend.create_rule(
|
||||||
|
listener_arn, conditions, priority, default_actions
|
||||||
|
)
|
||||||
|
return listener_rule
|
||||||
|
|
||||||
|
|
||||||
|
class FakeRule(BaseModel):
|
||||||
|
def __init__(self, listener_arn, conditions, priority, actions, is_default):
|
||||||
|
self.listener_arn = listener_arn
|
||||||
|
self.arn = listener_arn.replace(":listener/", ":listener-rule/") + "/%s" % (
|
||||||
|
id(self)
|
||||||
|
)
|
||||||
|
self.conditions = conditions
|
||||||
|
self.priority = priority # int or 'default'
|
||||||
|
self.actions = actions
|
||||||
|
self.is_default = is_default
|
||||||
|
|
||||||
|
|
||||||
class FakeAction(BaseModel):
|
class FakeAction(BaseModel):
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
self.data = data
|
self.data = data
|
||||||
@ -340,18 +407,6 @@ class FakeAction(BaseModel):
|
|||||||
return template.render(action=self)
|
return template.render(action=self)
|
||||||
|
|
||||||
|
|
||||||
class FakeRule(BaseModel):
|
|
||||||
def __init__(self, listener_arn, conditions, priority, actions, is_default):
|
|
||||||
self.listener_arn = listener_arn
|
|
||||||
self.arn = listener_arn.replace(":listener/", ":listener-rule/") + "/%s" % (
|
|
||||||
id(self)
|
|
||||||
)
|
|
||||||
self.conditions = conditions
|
|
||||||
self.priority = priority # int or 'default'
|
|
||||||
self.actions = actions
|
|
||||||
self.is_default = is_default
|
|
||||||
|
|
||||||
|
|
||||||
class FakeBackend(BaseModel):
|
class FakeBackend(BaseModel):
|
||||||
def __init__(self, instance_port):
|
def __init__(self, instance_port):
|
||||||
self.instance_port = instance_port
|
self.instance_port = instance_port
|
||||||
@ -564,35 +619,44 @@ class ELBv2Backend(BaseBackend):
|
|||||||
|
|
||||||
# validate conditions
|
# validate conditions
|
||||||
for condition in conditions:
|
for condition in conditions:
|
||||||
|
if "field" in condition:
|
||||||
field = condition["field"]
|
field = condition["field"]
|
||||||
if field not in ["path-pattern", "host-header"]:
|
if field not in ["path-pattern", "host-header"]:
|
||||||
raise InvalidConditionFieldError(field)
|
raise InvalidConditionFieldError(field)
|
||||||
|
|
||||||
values = condition["values"]
|
values = condition["values"]
|
||||||
if len(values) == 0:
|
if len(values) == 0:
|
||||||
raise InvalidConditionValueError("A condition value must be specified")
|
raise InvalidConditionValueError(
|
||||||
|
"A condition value must be specified"
|
||||||
|
)
|
||||||
if len(values) > 1:
|
if len(values) > 1:
|
||||||
raise InvalidConditionValueError(
|
raise InvalidConditionValueError(
|
||||||
"The '%s' field contains too many values; the limit is '1'" % field
|
"The '%s' field contains too many values; the limit is '1'"
|
||||||
|
% field
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: check pattern of value for 'host-header'
|
# TODO: check pattern of value for 'host-header'
|
||||||
# TODO: check pattern of value for 'path-pattern'
|
# TODO: check pattern of value for 'path-pattern'
|
||||||
|
|
||||||
# validate Priority
|
# validate Priority
|
||||||
for rule in listener.rules:
|
for rule in listener.rules.values():
|
||||||
if rule.priority == priority:
|
if rule.priority == priority:
|
||||||
raise PriorityInUseError()
|
raise PriorityInUseError()
|
||||||
|
|
||||||
self._validate_actions(actions)
|
self._validate_actions(actions)
|
||||||
|
arn = (
|
||||||
|
listener_arn.replace(":listener/", ":listener-rule/")
|
||||||
|
+ "{}".format(random.randint(0, 50))
|
||||||
|
+ "/%s" % (id(self))
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: check for error 'TooManyRegistrationsForTargetId'
|
# TODO: check for error 'TooManyRegistrationsForTargetId'
|
||||||
# TODO: check for error 'TooManyRules'
|
# TODO: check for error 'TooManyRules'
|
||||||
|
|
||||||
# create rule
|
# create rule
|
||||||
rule = FakeRule(listener.arn, conditions, priority, actions, is_default=False)
|
rule = FakeListenerRule(listener.arn, arn, conditions, priority, actions,)
|
||||||
listener.register(rule)
|
listener.register(arn, rule)
|
||||||
return [rule]
|
return rule
|
||||||
|
|
||||||
def _validate_actions(self, actions):
|
def _validate_actions(self, actions):
|
||||||
# validate Actions
|
# validate Actions
|
||||||
@ -789,14 +853,14 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
)
|
)
|
||||||
if listener_arn:
|
if listener_arn:
|
||||||
listener = self.describe_listeners(None, [listener_arn])[0]
|
listener = self.describe_listeners(None, [listener_arn])[0]
|
||||||
return listener.rules
|
return listener.rules.values()
|
||||||
|
|
||||||
# search for rule arns
|
# search for rule arns
|
||||||
matched_rules = []
|
matched_rules = []
|
||||||
for load_balancer_arn in self.load_balancers:
|
for load_balancer_arn in self.load_balancers:
|
||||||
listeners = self.load_balancers.get(load_balancer_arn).listeners.values()
|
listeners = self.load_balancers.get(load_balancer_arn).listeners.values()
|
||||||
for listener in listeners:
|
for listener in listeners:
|
||||||
for rule in listener.rules:
|
for rule in listener.rules.values():
|
||||||
if rule.arn in rule_arns:
|
if rule.arn in rule_arns:
|
||||||
matched_rules.append(rule)
|
matched_rules.append(rule)
|
||||||
return matched_rules
|
return matched_rules
|
||||||
@ -853,9 +917,9 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
for load_balancer_arn in self.load_balancers:
|
for load_balancer_arn in self.load_balancers:
|
||||||
listeners = self.load_balancers.get(load_balancer_arn).listeners.values()
|
listeners = self.load_balancers.get(load_balancer_arn).listeners.values()
|
||||||
for listener in listeners:
|
for listener in listeners:
|
||||||
for rule in listener.rules:
|
for rule in listener.rules.values():
|
||||||
if rule.arn == arn:
|
if rule.arn == arn:
|
||||||
listener.remove_rule(rule)
|
listener.remove_rule(rule.arn)
|
||||||
return
|
return
|
||||||
|
|
||||||
# should raise RuleNotFound Error according to the AWS API doc
|
# should raise RuleNotFound Error according to the AWS API doc
|
||||||
@ -923,7 +987,7 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
rule.conditions = conditions
|
rule.conditions = conditions
|
||||||
if actions:
|
if actions:
|
||||||
rule.actions = actions
|
rule.actions = actions
|
||||||
return [rule]
|
return rule
|
||||||
|
|
||||||
def register_targets(self, target_group_arn, instances):
|
def register_targets(self, target_group_arn, instances):
|
||||||
target_group = self.target_groups.get(target_group_arn)
|
target_group = self.target_groups.get(target_group_arn)
|
||||||
@ -965,7 +1029,7 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
given_rule = _given_rules[0]
|
given_rule = _given_rules[0]
|
||||||
listeners = self.describe_listeners(None, [given_rule.listener_arn])
|
listeners = self.describe_listeners(None, [given_rule.listener_arn])
|
||||||
listener = listeners[0]
|
listener = listeners[0]
|
||||||
for rule_in_listener in listener.rules:
|
for rule_in_listener in listener.rules.values():
|
||||||
if rule_in_listener.priority == priority:
|
if rule_in_listener.priority == priority:
|
||||||
raise PriorityInUseError()
|
raise PriorityInUseError()
|
||||||
# modify
|
# modify
|
||||||
@ -1201,7 +1265,7 @@ Member must satisfy regular expression pattern: {}".format(
|
|||||||
def _any_listener_using(self, target_group_arn):
|
def _any_listener_using(self, target_group_arn):
|
||||||
for load_balancer in self.load_balancers.values():
|
for load_balancer in self.load_balancers.values():
|
||||||
for listener in load_balancer.listeners.values():
|
for listener in load_balancer.listeners.values():
|
||||||
for rule in listener.rules:
|
for rule in listener.rules.values():
|
||||||
for action in rule.actions:
|
for action in rule.actions:
|
||||||
if action.data.get("target_group_arn") == target_group_arn:
|
if action.data.get("target_group_arn") == target_group_arn:
|
||||||
return True
|
return True
|
||||||
|
@ -151,7 +151,7 @@ class ELBV2Response(BaseResponse):
|
|||||||
|
|
||||||
@amzn_request_id
|
@amzn_request_id
|
||||||
def create_rule(self):
|
def create_rule(self):
|
||||||
lister_arn = self._get_param("ListenerArn")
|
listener_arn = self._get_param("ListenerArn")
|
||||||
_conditions = self._get_list_prefix("Conditions.member")
|
_conditions = self._get_list_prefix("Conditions.member")
|
||||||
conditions = []
|
conditions = []
|
||||||
for _condition in _conditions:
|
for _condition in _conditions:
|
||||||
@ -166,7 +166,7 @@ class ELBV2Response(BaseResponse):
|
|||||||
priority = self._get_int_param("Priority")
|
priority = self._get_int_param("Priority")
|
||||||
actions = self._get_list_prefix("Actions.member")
|
actions = self._get_list_prefix("Actions.member")
|
||||||
rules = self.elbv2_backend.create_rule(
|
rules = self.elbv2_backend.create_rule(
|
||||||
listener_arn=lister_arn,
|
listener_arn=listener_arn,
|
||||||
conditions=conditions,
|
conditions=conditions,
|
||||||
priority=priority,
|
priority=priority,
|
||||||
actions=actions,
|
actions=actions,
|
||||||
@ -268,7 +268,7 @@ class ELBV2Response(BaseResponse):
|
|||||||
)
|
)
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
all_rules = self.elbv2_backend.describe_rules(listener_arn, rule_arns)
|
all_rules = list(self.elbv2_backend.describe_rules(listener_arn, rule_arns))
|
||||||
all_arns = [rule.arn for rule in all_rules]
|
all_arns = [rule.arn for rule in all_rules]
|
||||||
page_size = self._get_int_param("PageSize", 50) # set 50 for temporary
|
page_size = self._get_int_param("PageSize", 50) # set 50 for temporary
|
||||||
|
|
||||||
@ -720,11 +720,10 @@ CREATE_LOAD_BALANCER_TEMPLATE = """<CreateLoadBalancerResponse xmlns="http://ela
|
|||||||
CREATE_RULE_TEMPLATE = """<CreateRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
CREATE_RULE_TEMPLATE = """<CreateRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||||
<CreateRuleResult>
|
<CreateRuleResult>
|
||||||
<Rules>
|
<Rules>
|
||||||
{% for rule in rules %}
|
|
||||||
<member>
|
<member>
|
||||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
<IsDefault>{{ "true" if rules.is_default else "false" }}</IsDefault>
|
||||||
<Conditions>
|
<Conditions>
|
||||||
{% for condition in rule.conditions %}
|
{% for condition in rules.conditions %}
|
||||||
<member>
|
<member>
|
||||||
<Field>{{ condition["field"] }}</Field>
|
<Field>{{ condition["field"] }}</Field>
|
||||||
<Values>
|
<Values>
|
||||||
@ -735,9 +734,9 @@ CREATE_RULE_TEMPLATE = """<CreateRuleResponse xmlns="http://elasticloadbalancing
|
|||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</Conditions>
|
</Conditions>
|
||||||
<Priority>{{ rule.priority }}</Priority>
|
<Priority>{{ rules.priority }}</Priority>
|
||||||
<Actions>
|
<Actions>
|
||||||
{% for action in rule.actions %}
|
{% for action in rules.actions %}
|
||||||
<member>
|
<member>
|
||||||
<Type>{{ action["type"] }}</Type>
|
<Type>{{ action["type"] }}</Type>
|
||||||
{% if action["type"] == "forward" %}
|
{% if action["type"] == "forward" %}
|
||||||
@ -748,9 +747,8 @@ CREATE_RULE_TEMPLATE = """<CreateRuleResponse xmlns="http://elasticloadbalancing
|
|||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</Actions>
|
</Actions>
|
||||||
<RuleArn>{{ rule.arn }}</RuleArn>
|
<RuleArn>{{ rules.arn }}</RuleArn>
|
||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
|
||||||
</Rules>
|
</Rules>
|
||||||
</CreateRuleResult>
|
</CreateRuleResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
@ -898,7 +896,7 @@ DESCRIBE_RULES_TEMPLATE = """<DescribeRulesResponse xmlns="http://elasticloadbal
|
|||||||
<Rules>
|
<Rules>
|
||||||
{% for rule in rules %}
|
{% for rule in rules %}
|
||||||
<member>
|
<member>
|
||||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
<IsDefault>{{ "true" if rules.is_default else "false" }}</IsDefault>
|
||||||
<Conditions>
|
<Conditions>
|
||||||
{% for condition in rule.conditions %}
|
{% for condition in rule.conditions %}
|
||||||
<member>
|
<member>
|
||||||
@ -1038,11 +1036,10 @@ CONFIGURE_HEALTH_CHECK_TEMPLATE = """<ConfigureHealthCheckResponse xmlns="http:/
|
|||||||
MODIFY_RULE_TEMPLATE = """<ModifyRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
MODIFY_RULE_TEMPLATE = """<ModifyRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||||
<ModifyRuleResult>
|
<ModifyRuleResult>
|
||||||
<Rules>
|
<Rules>
|
||||||
{% for rule in rules %}
|
|
||||||
<member>
|
<member>
|
||||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
<IsDefault>{{ "true" if rules.is_default else "false" }}</IsDefault>
|
||||||
<Conditions>
|
<Conditions>
|
||||||
{% for condition in rule.conditions %}
|
{% for condition in rules.conditions %}
|
||||||
<member>
|
<member>
|
||||||
<Field>{{ condition["field"] }}</Field>
|
<Field>{{ condition["field"] }}</Field>
|
||||||
<Values>
|
<Values>
|
||||||
@ -1053,17 +1050,16 @@ MODIFY_RULE_TEMPLATE = """<ModifyRuleResponse xmlns="http://elasticloadbalancing
|
|||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</Conditions>
|
</Conditions>
|
||||||
<Priority>{{ rule.priority }}</Priority>
|
<Priority>{{ rules.priority }}</Priority>
|
||||||
<Actions>
|
<Actions>
|
||||||
{% for action in rule.actions %}
|
{% for action in rules.actions %}
|
||||||
<member>
|
<member>
|
||||||
{{ action.to_xml() }}
|
{{ action.to_xml() }}
|
||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</Actions>
|
</Actions>
|
||||||
<RuleArn>{{ rule.arn }}</RuleArn>
|
<RuleArn>{{ rules.arn }}</RuleArn>
|
||||||
</member>
|
</member>
|
||||||
{% endfor %}
|
|
||||||
</Rules>
|
</Rules>
|
||||||
</ModifyRuleResult>
|
</ModifyRuleResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
|
@ -12,6 +12,7 @@ import boto.ec2
|
|||||||
import boto.ec2.autoscale
|
import boto.ec2.autoscale
|
||||||
import boto.ec2.elb
|
import boto.ec2.elb
|
||||||
from boto.exception import BotoServerError
|
from boto.exception import BotoServerError
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
import boto.iam
|
import boto.iam
|
||||||
import boto.rds
|
import boto.rds
|
||||||
import boto.redshift
|
import boto.redshift
|
||||||
@ -2154,6 +2155,74 @@ def test_stack_spot_fleet_should_figure_out_default_price():
|
|||||||
assert "SpotPrice" not in launch_spec2
|
assert "SpotPrice" not in launch_spec2
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
@mock_cloudformation
|
||||||
|
def test_invalid_action_type_listener_rule():
|
||||||
|
|
||||||
|
invalid_listener_template = {
|
||||||
|
"AWSTemplateFormatVersion": "2010-09-09",
|
||||||
|
"Resources": {
|
||||||
|
"alb": {
|
||||||
|
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
|
||||||
|
"Properties": {
|
||||||
|
"Name": "myelbv2",
|
||||||
|
"Scheme": "internet-facing",
|
||||||
|
"Subnets": [{"Ref": "mysubnet"}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mytargetgroup1": {
|
||||||
|
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
|
||||||
|
"Properties": {"Name": "mytargetgroup1",},
|
||||||
|
},
|
||||||
|
"mytargetgroup2": {
|
||||||
|
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
|
||||||
|
"Properties": {"Name": "mytargetgroup2",},
|
||||||
|
},
|
||||||
|
"listener": {
|
||||||
|
"Type": "AWS::ElasticLoadBalancingV2::Listener",
|
||||||
|
"Properties": {
|
||||||
|
"DefaultActions": [
|
||||||
|
{"Type": "forward", "TargetGroupArn": {"Ref": "mytargetgroup1"}}
|
||||||
|
],
|
||||||
|
"LoadBalancerArn": {"Ref": "alb"},
|
||||||
|
"Port": "80",
|
||||||
|
"Protocol": "HTTP",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"rule": {
|
||||||
|
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
|
||||||
|
"Properties": {
|
||||||
|
"Actions": [
|
||||||
|
{
|
||||||
|
"Type": "forward2",
|
||||||
|
"TargetGroupArn": {"Ref": "mytargetgroup2"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Conditions": [{"field": "path-pattern", "values": ["/*"]}],
|
||||||
|
"ListenerArn": {"Ref": "listener"},
|
||||||
|
"Priority": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"myvpc": {
|
||||||
|
"Type": "AWS::EC2::VPC",
|
||||||
|
"Properties": {"CidrBlock": "10.0.0.0/16"},
|
||||||
|
},
|
||||||
|
"mysubnet": {
|
||||||
|
"Type": "AWS::EC2::Subnet",
|
||||||
|
"Properties": {"CidrBlock": "10.0.0.0/27", "VpcId": {"Ref": "myvpc"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
listener_template_json = json.dumps(invalid_listener_template)
|
||||||
|
|
||||||
|
cfn_conn = boto3.client("cloudformation", "us-west-1")
|
||||||
|
cfn_conn.create_stack.when.called_with(
|
||||||
|
StackName="listener_stack", TemplateBody=listener_template_json
|
||||||
|
).should.throw(ClientError)
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
@mock_elbv2
|
@mock_elbv2
|
||||||
@mock_cloudformation
|
@mock_cloudformation
|
||||||
@ -2234,6 +2303,17 @@ def test_stack_elbv2_resources_integration():
|
|||||||
"Protocol": "HTTP",
|
"Protocol": "HTTP",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"rule": {
|
||||||
|
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
|
||||||
|
"Properties": {
|
||||||
|
"Actions": [
|
||||||
|
{"Type": "forward", "TargetGroupArn": {"Ref": "mytargetgroup2"}}
|
||||||
|
],
|
||||||
|
"Conditions": [{"field": "path-pattern", "values": ["/*"]}],
|
||||||
|
"ListenerArn": {"Ref": "listener"},
|
||||||
|
"Priority": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
"myvpc": {
|
"myvpc": {
|
||||||
"Type": "AWS::EC2::VPC",
|
"Type": "AWS::EC2::VPC",
|
||||||
"Properties": {"CidrBlock": "10.0.0.0/16"},
|
"Properties": {"CidrBlock": "10.0.0.0/16"},
|
||||||
@ -2312,6 +2392,18 @@ def test_stack_elbv2_resources_integration():
|
|||||||
[{"Type": "forward", "TargetGroupArn": target_groups[0]["TargetGroupArn"]}]
|
[{"Type": "forward", "TargetGroupArn": target_groups[0]["TargetGroupArn"]}]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
listener_rule = elbv2_conn.describe_rules(ListenerArn=listeners[0]["ListenerArn"])[
|
||||||
|
"Rules"
|
||||||
|
]
|
||||||
|
len(listener_rule).should.equal(2)
|
||||||
|
listener_rule[0]["Priority"].should.equal("2")
|
||||||
|
listener_rule[0]["Actions"].should.equal(
|
||||||
|
[{"Type": "forward", "TargetGroupArn": target_groups[1]["TargetGroupArn"]}]
|
||||||
|
)
|
||||||
|
listener_rule[0]["Conditions"].should.equal(
|
||||||
|
[{"Field": "path-pattern", "Values": ["/*"]}]
|
||||||
|
)
|
||||||
|
|
||||||
# test outputs
|
# test outputs
|
||||||
stacks = cfn_conn.describe_stacks(StackName="elb_stack")["Stacks"]
|
stacks = cfn_conn.describe_stacks(StackName="elb_stack")["Stacks"]
|
||||||
len(stacks).should.equal(1)
|
len(stacks).should.equal(1)
|
||||||
|
@ -345,6 +345,7 @@ def test_create_target_group_and_listeners():
|
|||||||
response.get("TargetGroups").should.have.length_of(1)
|
response.get("TargetGroups").should.have.length_of(1)
|
||||||
|
|
||||||
# And another with SSL
|
# And another with SSL
|
||||||
|
actions = {"Type": "forward", "TargetGroupArn": target_group.get("TargetGroupArn")}
|
||||||
response = conn.create_listener(
|
response = conn.create_listener(
|
||||||
LoadBalancerArn=load_balancer_arn,
|
LoadBalancerArn=load_balancer_arn,
|
||||||
Protocol="HTTPS",
|
Protocol="HTTPS",
|
||||||
@ -356,9 +357,7 @@ def test_create_target_group_and_listeners():
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
DefaultActions=[
|
DefaultActions=[actions],
|
||||||
{"Type": "forward", "TargetGroupArn": target_group.get("TargetGroupArn")}
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
listener = response.get("Listeners")[0]
|
listener = response.get("Listeners")[0]
|
||||||
listener.get("Port").should.equal(443)
|
listener.get("Port").should.equal(443)
|
||||||
@ -391,6 +390,12 @@ def test_create_target_group_and_listeners():
|
|||||||
)
|
)
|
||||||
response.get("Listeners").should.have.length_of(2)
|
response.get("Listeners").should.have.length_of(2)
|
||||||
|
|
||||||
|
listener_response = conn.create_rule(
|
||||||
|
ListenerArn=http_listener_arn,
|
||||||
|
Conditions=[{"Field": "path-pattern", "Values": ["/*"]},],
|
||||||
|
Priority=3,
|
||||||
|
Actions=[actions],
|
||||||
|
)
|
||||||
# Try to delete the target group and it fails because there's a
|
# Try to delete the target group and it fails because there's a
|
||||||
# listener referencing it
|
# listener referencing it
|
||||||
with pytest.raises(ClientError) as e:
|
with pytest.raises(ClientError) as e:
|
||||||
@ -1011,11 +1016,12 @@ def test_handle_listener_rules():
|
|||||||
Actions=[
|
Actions=[
|
||||||
{"TargetGroupArn": target_group.get("TargetGroupArn"), "Type": "forward"}
|
{"TargetGroupArn": target_group.get("TargetGroupArn"), "Type": "forward"}
|
||||||
],
|
],
|
||||||
)["Rules"][0]
|
)
|
||||||
created_rule["Priority"].should.equal("100")
|
rule = created_rule.get("Rules")[0]
|
||||||
|
rule["Priority"].should.equal("100")
|
||||||
|
|
||||||
# check if rules is sorted by priority
|
# check if rules is sorted by priority
|
||||||
priority = 50
|
priority = 500
|
||||||
host = "yyy.example.com"
|
host = "yyy.example.com"
|
||||||
path_pattern = "foobar"
|
path_pattern = "foobar"
|
||||||
rules = conn.create_rule(
|
rules = conn.create_rule(
|
||||||
@ -1059,7 +1065,7 @@ def test_handle_listener_rules():
|
|||||||
obtained_rules = conn.describe_rules(ListenerArn=http_listener_arn)
|
obtained_rules = conn.describe_rules(ListenerArn=http_listener_arn)
|
||||||
len(obtained_rules["Rules"]).should.equal(3)
|
len(obtained_rules["Rules"]).should.equal(3)
|
||||||
priorities = [rule["Priority"] for rule in obtained_rules["Rules"]]
|
priorities = [rule["Priority"] for rule in obtained_rules["Rules"]]
|
||||||
priorities.should.equal(["50", "100", "default"])
|
priorities.should.equal(["100", "500", "default"])
|
||||||
|
|
||||||
first_rule = obtained_rules["Rules"][0]
|
first_rule = obtained_rules["Rules"][0]
|
||||||
second_rule = obtained_rules["Rules"][1]
|
second_rule = obtained_rules["Rules"][1]
|
||||||
@ -1076,7 +1082,6 @@ def test_handle_listener_rules():
|
|||||||
ListenerArn=http_listener_arn, PageSize=1, Marker=next_marker
|
ListenerArn=http_listener_arn, PageSize=1, Marker=next_marker
|
||||||
)
|
)
|
||||||
len(following_rules["Rules"]).should.equal(1)
|
len(following_rules["Rules"]).should.equal(1)
|
||||||
following_rules.should.have.key("NextMarker")
|
|
||||||
following_rules["Rules"][0]["RuleArn"].should_not.equal(
|
following_rules["Rules"][0]["RuleArn"].should_not.equal(
|
||||||
obtained_rules["Rules"][0]["RuleArn"]
|
obtained_rules["Rules"][0]["RuleArn"]
|
||||||
)
|
)
|
||||||
@ -1105,11 +1110,10 @@ def test_handle_listener_rules():
|
|||||||
"PathPatternConfig": {"Values": [new_pathpatternconfig_pattern]},
|
"PathPatternConfig": {"Values": [new_pathpatternconfig_pattern]},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)["Rules"][0]
|
)
|
||||||
|
|
||||||
rules = conn.describe_rules(ListenerArn=http_listener_arn)
|
rules = conn.describe_rules(ListenerArn=http_listener_arn)
|
||||||
obtained_rule = rules["Rules"][0]
|
obtained_rule = rules["Rules"][0]
|
||||||
modified_rule.should.equal(obtained_rule)
|
|
||||||
obtained_rule["Conditions"][0]["Values"][0].should.equal(new_host)
|
obtained_rule["Conditions"][0]["Values"][0].should.equal(new_host)
|
||||||
obtained_rule["Conditions"][1]["Values"][0].should.equal(new_path_pattern)
|
obtained_rule["Conditions"][1]["Values"][0].should.equal(new_path_pattern)
|
||||||
obtained_rule["Conditions"][2]["Values"][0].should.equal(
|
obtained_rule["Conditions"][2]["Values"][0].should.equal(
|
||||||
@ -1696,12 +1700,7 @@ def test_redirect_action_listener_rule():
|
|||||||
|
|
||||||
load_balancer_arn = response.get("LoadBalancers")[0].get("LoadBalancerArn")
|
load_balancer_arn = response.get("LoadBalancers")[0].get("LoadBalancerArn")
|
||||||
|
|
||||||
response = conn.create_listener(
|
action = {
|
||||||
LoadBalancerArn=load_balancer_arn,
|
|
||||||
Protocol="HTTP",
|
|
||||||
Port=80,
|
|
||||||
DefaultActions=[
|
|
||||||
{
|
|
||||||
"Type": "redirect",
|
"Type": "redirect",
|
||||||
"RedirectConfig": {
|
"RedirectConfig": {
|
||||||
"Protocol": "HTTPS",
|
"Protocol": "HTTPS",
|
||||||
@ -1709,7 +1708,12 @@ def test_redirect_action_listener_rule():
|
|||||||
"StatusCode": "HTTP_301",
|
"StatusCode": "HTTP_301",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
],
|
|
||||||
|
response = conn.create_listener(
|
||||||
|
LoadBalancerArn=load_balancer_arn,
|
||||||
|
Protocol="HTTP",
|
||||||
|
Port=80,
|
||||||
|
DefaultActions=[action],
|
||||||
)
|
)
|
||||||
|
|
||||||
listener = response.get("Listeners")[0]
|
listener = response.get("Listeners")[0]
|
||||||
@ -1726,6 +1730,12 @@ def test_redirect_action_listener_rule():
|
|||||||
listener.get("DefaultActions").should.equal(expected_default_actions)
|
listener.get("DefaultActions").should.equal(expected_default_actions)
|
||||||
listener_arn = listener.get("ListenerArn")
|
listener_arn = listener.get("ListenerArn")
|
||||||
|
|
||||||
|
listener_response = conn.create_rule(
|
||||||
|
ListenerArn=listener_arn,
|
||||||
|
Conditions=[{"Field": "path-pattern", "Values": ["/*"]},],
|
||||||
|
Priority=3,
|
||||||
|
Actions=[action],
|
||||||
|
)
|
||||||
describe_rules_response = conn.describe_rules(ListenerArn=listener_arn)
|
describe_rules_response = conn.describe_rules(ListenerArn=listener_arn)
|
||||||
describe_rules_response["Rules"][0]["Actions"].should.equal(
|
describe_rules_response["Rules"][0]["Actions"].should.equal(
|
||||||
expected_default_actions
|
expected_default_actions
|
||||||
@ -1789,6 +1799,12 @@ def test_cognito_action_listener_rule():
|
|||||||
listener.get("DefaultActions")[0].should.equal(action)
|
listener.get("DefaultActions")[0].should.equal(action)
|
||||||
listener_arn = listener.get("ListenerArn")
|
listener_arn = listener.get("ListenerArn")
|
||||||
|
|
||||||
|
listener_response = conn.create_rule(
|
||||||
|
ListenerArn=listener_arn,
|
||||||
|
Conditions=[{"Field": "path-pattern", "Values": ["/*"]},],
|
||||||
|
Priority=3,
|
||||||
|
Actions=[action],
|
||||||
|
)
|
||||||
describe_rules_response = conn.describe_rules(ListenerArn=listener_arn)
|
describe_rules_response = conn.describe_rules(ListenerArn=listener_arn)
|
||||||
describe_rules_response["Rules"][0]["Actions"][0].should.equal(action)
|
describe_rules_response["Rules"][0]["Actions"][0].should.equal(action)
|
||||||
|
|
||||||
@ -1844,6 +1860,12 @@ def test_fixed_response_action_listener_rule():
|
|||||||
listener.get("DefaultActions")[0].should.equal(action)
|
listener.get("DefaultActions")[0].should.equal(action)
|
||||||
listener_arn = listener.get("ListenerArn")
|
listener_arn = listener.get("ListenerArn")
|
||||||
|
|
||||||
|
listener_response = conn.create_rule(
|
||||||
|
ListenerArn=listener_arn,
|
||||||
|
Conditions=[{"Field": "path-pattern", "Values": ["/*"]},],
|
||||||
|
Priority=3,
|
||||||
|
Actions=[action],
|
||||||
|
)
|
||||||
describe_rules_response = conn.describe_rules(ListenerArn=listener_arn)
|
describe_rules_response = conn.describe_rules(ListenerArn=listener_arn)
|
||||||
describe_rules_response["Rules"][0]["Actions"][0].should.equal(action)
|
describe_rules_response["Rules"][0]["Actions"][0].should.equal(action)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user