Merge pull request #1057 from kawasakitoshiya/feature/add_elbv2_listener_rules
Implement methods related to listener rules at ELBv2
This commit is contained in:
commit
b75f85690e
@ -101,3 +101,68 @@ class EmptyListenersError(ELBClientError):
|
||||
super(EmptyListenersError, self).__init__(
|
||||
"ValidationError",
|
||||
"Listeners cannot be empty")
|
||||
|
||||
|
||||
class PriorityInUseError(ELBClientError):
|
||||
|
||||
def __init__(self):
|
||||
super(PriorityInUseError, self).__init__(
|
||||
"PriorityInUse",
|
||||
"The specified priority is in use.")
|
||||
|
||||
|
||||
class InvalidConditionFieldError(ELBClientError):
|
||||
|
||||
def __init__(self, invalid_name):
|
||||
super(InvalidConditionFieldError, self).__init__(
|
||||
"ValidationError",
|
||||
"Condition field '%s' must be one of '[path-pattern, host-header]" % (invalid_name))
|
||||
|
||||
|
||||
class InvalidConditionValueError(ELBClientError):
|
||||
|
||||
def __init__(self, msg):
|
||||
super(InvalidConditionValueError, self).__init__(
|
||||
"ValidationError", msg)
|
||||
|
||||
|
||||
class InvalidActionTypeError(ELBClientError):
|
||||
|
||||
def __init__(self, invalid_name, index):
|
||||
super(InvalidActionTypeError, self).__init__(
|
||||
"ValidationError",
|
||||
"1 validation error detected: Value '%s' at 'actions.%s.member.type' failed to satisfy constraint: Member must satisfy enum value set: [forward]" % (invalid_name, index)
|
||||
)
|
||||
|
||||
|
||||
class ActionTargetGroupNotFoundError(ELBClientError):
|
||||
|
||||
def __init__(self, arn):
|
||||
super(ActionTargetGroupNotFoundError, self).__init__(
|
||||
"TargetGroupNotFound",
|
||||
"Target group '%s' not found" % arn
|
||||
)
|
||||
|
||||
|
||||
class InvalidDescribeRulesRequest(ELBClientError):
|
||||
|
||||
def __init__(self, msg):
|
||||
super(InvalidDescribeRulesRequest, self).__init__(
|
||||
"ValidationError", msg
|
||||
)
|
||||
|
||||
|
||||
class RuleNotFoundError(ELBClientError):
|
||||
|
||||
def __init__(self):
|
||||
super(RuleNotFoundError, self).__init__(
|
||||
"RuleNotFound",
|
||||
"The specified rule does not exist.")
|
||||
|
||||
|
||||
class DuplicatePriorityError(ELBClientError):
|
||||
|
||||
def __init__(self, invalid_value):
|
||||
super(DuplicatePriorityError, self).__init__(
|
||||
"ValidationError",
|
||||
"Priority '%s' was provided multiple times" % invalid_value)
|
||||
|
@ -14,6 +14,14 @@ from .exceptions import (
|
||||
SubnetNotFoundError,
|
||||
TargetGroupNotFoundError,
|
||||
TooManyTagsError,
|
||||
PriorityInUseError,
|
||||
InvalidConditionFieldError,
|
||||
InvalidConditionValueError,
|
||||
InvalidActionTypeError,
|
||||
ActionTargetGroupNotFoundError,
|
||||
InvalidDescribeRulesRequest,
|
||||
RuleNotFoundError,
|
||||
DuplicatePriorityError
|
||||
)
|
||||
|
||||
|
||||
@ -92,6 +100,33 @@ class FakeListener(BaseModel):
|
||||
self.ssl_policy = ssl_policy
|
||||
self.certificate = certificate
|
||||
self.default_actions = default_actions
|
||||
self._non_default_rules = []
|
||||
self._default_rule = FakeRule(
|
||||
listener_arn=self.arn,
|
||||
conditions=[],
|
||||
priority='default',
|
||||
actions=default_actions,
|
||||
is_default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def rules(self):
|
||||
return self._non_default_rules + [self._default_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)
|
||||
|
||||
|
||||
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):
|
||||
@ -181,6 +216,53 @@ class ELBv2Backend(BaseBackend):
|
||||
self.load_balancers[arn] = new_load_balancer
|
||||
return new_load_balancer
|
||||
|
||||
def create_rule(self, listener_arn, conditions, priority, actions):
|
||||
listeners = self.describe_listeners(None, [listener_arn])
|
||||
if not listeners:
|
||||
raise ListenerNotFoundError()
|
||||
listener = listeners[0]
|
||||
|
||||
# validate conditions
|
||||
for condition in conditions:
|
||||
field = condition['field']
|
||||
if field not in ['path-pattern', 'host-header']:
|
||||
raise InvalidConditionFieldError(field)
|
||||
|
||||
values = condition['values']
|
||||
if len(values) == 0:
|
||||
raise InvalidConditionValueError('A condition value must be specified')
|
||||
if len(values) > 1:
|
||||
raise InvalidConditionValueError(
|
||||
"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 'path-pattern'
|
||||
|
||||
# validate Priority
|
||||
for rule in listener.rules:
|
||||
if rule.priority == priority:
|
||||
raise PriorityInUseError()
|
||||
|
||||
# validate Actions
|
||||
target_group_arns = [target_group.arn for target_group in self.target_groups.values()]
|
||||
for i, action in enumerate(actions):
|
||||
index = i + 1
|
||||
action_type = action['type']
|
||||
if action_type not in ['forward']:
|
||||
raise InvalidActionTypeError(action_type, index)
|
||||
action_target_group_arn = action['target_group_arn']
|
||||
if action_target_group_arn not in target_group_arns:
|
||||
raise ActionTargetGroupNotFoundError(action_target_group_arn)
|
||||
|
||||
# TODO: check for error 'TooManyRegistrationsForTargetId'
|
||||
# TODO: check for error 'TooManyRules'
|
||||
|
||||
# create rule
|
||||
rule = FakeRule(listener.arn, conditions, priority, actions, is_default=False)
|
||||
listener.register(rule)
|
||||
return [rule]
|
||||
|
||||
def create_target_group(self, name, **kwargs):
|
||||
for target_group in self.target_groups.values():
|
||||
if target_group.name == name:
|
||||
@ -233,6 +315,29 @@ class ELBv2Backend(BaseBackend):
|
||||
|
||||
return matched_balancers
|
||||
|
||||
def describe_rules(self, listener_arn, rule_arns):
|
||||
if listener_arn is None and not rule_arns:
|
||||
raise InvalidDescribeRulesRequest(
|
||||
"You must specify either listener rule ARNs or a listener ARN"
|
||||
)
|
||||
if listener_arn is not None and rule_arns is not None:
|
||||
raise InvalidDescribeRulesRequest(
|
||||
'Listener rule ARNs and a listener ARN cannot be specified at the same time'
|
||||
)
|
||||
if listener_arn:
|
||||
listener = self.describe_listeners(None, [listener_arn])[0]
|
||||
return listener.rules
|
||||
|
||||
# search for rule arns
|
||||
matched_rules = []
|
||||
for load_balancer_arn in self.load_balancers:
|
||||
listeners = self.load_balancers.get(load_balancer_arn).listeners.values()
|
||||
for listener in listeners:
|
||||
for rule in listener.rules:
|
||||
if rule.arn in rule_arns:
|
||||
matched_rules.append(rule)
|
||||
return matched_rules
|
||||
|
||||
def describe_target_groups(self, load_balancer_arn, target_group_arns, names):
|
||||
if load_balancer_arn:
|
||||
if load_balancer_arn not in self.load_balancers:
|
||||
@ -277,6 +382,18 @@ class ELBv2Backend(BaseBackend):
|
||||
def delete_load_balancer(self, arn):
|
||||
self.load_balancers.pop(arn, None)
|
||||
|
||||
def delete_rule(self, arn):
|
||||
for load_balancer_arn in self.load_balancers:
|
||||
listeners = self.load_balancers.get(load_balancer_arn).listeners.values()
|
||||
for listener in listeners:
|
||||
for rule in listener.rules:
|
||||
if rule.arn == arn:
|
||||
listener.rules.remove(rule)
|
||||
return
|
||||
|
||||
# should raise RuleNotFound Error according to the AWS API doc
|
||||
# however, boto3 does't raise error even if rule is not found
|
||||
|
||||
def delete_target_group(self, target_group_arn):
|
||||
target_group = self.target_groups.pop(target_group_arn)
|
||||
if target_group:
|
||||
@ -290,6 +407,48 @@ class ELBv2Backend(BaseBackend):
|
||||
return listener
|
||||
raise ListenerNotFoundError()
|
||||
|
||||
def modify_rule(self, rule_arn, conditions, actions):
|
||||
rules = self.describe_rules(listener_arn=None, rule_arns=[rule_arn])
|
||||
if not rules:
|
||||
raise RuleNotFoundError()
|
||||
rule = rules[0]
|
||||
|
||||
# validate conditions
|
||||
for condition in conditions:
|
||||
field = condition['field']
|
||||
if field not in ['path-pattern', 'host-header']:
|
||||
raise InvalidConditionFieldError(field)
|
||||
|
||||
values = condition['values']
|
||||
if len(values) == 0:
|
||||
raise InvalidConditionValueError('A condition value must be specified')
|
||||
if len(values) > 1:
|
||||
raise InvalidConditionValueError(
|
||||
"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 'path-pattern'
|
||||
|
||||
# validate Actions
|
||||
target_group_arns = [target_group.arn for target_group in self.target_groups.values()]
|
||||
for i, action in enumerate(actions):
|
||||
index = i + 1
|
||||
action_type = action['type']
|
||||
if action_type not in ['forward']:
|
||||
raise InvalidActionTypeError(action_type, index)
|
||||
action_target_group_arn = action['target_group_arn']
|
||||
if action_target_group_arn not in target_group_arns:
|
||||
raise ActionTargetGroupNotFoundError(action_target_group_arn)
|
||||
|
||||
# TODO: check for error 'TooManyRegistrationsForTargetId'
|
||||
# TODO: check for error 'TooManyRules'
|
||||
|
||||
# modify rule
|
||||
rule.conditions = conditions
|
||||
rule.actions = actions
|
||||
return [rule]
|
||||
|
||||
def register_targets(self, target_group_arn, instances):
|
||||
target_group = self.target_groups.get(target_group_arn)
|
||||
if target_group is None:
|
||||
@ -311,6 +470,39 @@ class ELBv2Backend(BaseBackend):
|
||||
targets = target_group.targets.values()
|
||||
return [target_group.health_for(target) for target in targets]
|
||||
|
||||
def set_rule_priorities(self, rule_priorities):
|
||||
# validate
|
||||
priorities = [rule_priority['priority'] for rule_priority in rule_priorities]
|
||||
for priority in set(priorities):
|
||||
if priorities.count(priority) > 1:
|
||||
raise DuplicatePriorityError(priority)
|
||||
|
||||
# validate
|
||||
for rule_priority in rule_priorities:
|
||||
given_rule_arn = rule_priority['rule_arn']
|
||||
priority = rule_priority['priority']
|
||||
_given_rules = self.describe_rules(listener_arn=None, rule_arns=[given_rule_arn])
|
||||
if not _given_rules:
|
||||
raise RuleNotFoundError()
|
||||
given_rule = _given_rules[0]
|
||||
listeners = self.describe_listeners(None, [given_rule.listener_arn])
|
||||
listener = listeners[0]
|
||||
for rule_in_listener in listener.rules:
|
||||
if rule_in_listener.priority == priority:
|
||||
raise PriorityInUseError()
|
||||
# modify
|
||||
modified_rules = []
|
||||
for rule_priority in rule_priorities:
|
||||
given_rule_arn = rule_priority['rule_arn']
|
||||
priority = rule_priority['priority']
|
||||
_given_rules = self.describe_rules(listener_arn=None, rule_arns=[given_rule_arn])
|
||||
if not _given_rules:
|
||||
raise RuleNotFoundError()
|
||||
given_rule = _given_rules[0]
|
||||
given_rule.priority = priority
|
||||
modified_rules.append(given_rule)
|
||||
return modified_rules
|
||||
|
||||
|
||||
elbv2_backends = {}
|
||||
for region in ec2_backends.keys():
|
||||
|
@ -28,6 +28,30 @@ class ELBV2Response(BaseResponse):
|
||||
template = self.response_template(CREATE_LOAD_BALANCER_TEMPLATE)
|
||||
return template.render(load_balancer=load_balancer)
|
||||
|
||||
def create_rule(self):
|
||||
lister_arn = self._get_param('ListenerArn')
|
||||
_conditions = self._get_list_prefix('Conditions.member')
|
||||
conditions = []
|
||||
for _condition in _conditions:
|
||||
condition = {}
|
||||
condition['field'] = _condition['field']
|
||||
values = sorted(
|
||||
[e for e in _condition.items() if e[0].startswith('values.member')],
|
||||
key=lambda x: x[0]
|
||||
)
|
||||
condition['values'] = [e[1] for e in values]
|
||||
conditions.append(condition)
|
||||
priority = self._get_int_param('Priority')
|
||||
actions = self._get_list_prefix('Actions.member')
|
||||
rules = self.elbv2_backend.create_rule(
|
||||
listener_arn=lister_arn,
|
||||
conditions=conditions,
|
||||
priority=priority,
|
||||
actions=actions
|
||||
)
|
||||
template = self.response_template(CREATE_RULE_TEMPLATE)
|
||||
return template.render(rules=rules)
|
||||
|
||||
def create_target_group(self):
|
||||
name = self._get_param('Name')
|
||||
vpc_id = self._get_param('VpcId')
|
||||
@ -100,6 +124,26 @@ class ELBV2Response(BaseResponse):
|
||||
template = self.response_template(DESCRIBE_LOAD_BALANCERS_TEMPLATE)
|
||||
return template.render(load_balancers=load_balancers_resp, marker=next_marker)
|
||||
|
||||
def describe_rules(self):
|
||||
listener_arn = self._get_param('ListenerArn')
|
||||
rule_arns = self._get_multi_param('RuleArns.member') if any(k for k in list(self.querystring.keys()) if k.startswith('RuleArns.member')) else None
|
||||
all_rules = self.elbv2_backend.describe_rules(listener_arn, rule_arns)
|
||||
all_arns = [rule.arn for rule in all_rules]
|
||||
page_size = self._get_int_param('PageSize', 50) # set 50 for temporary
|
||||
|
||||
marker = self._get_param('Marker')
|
||||
if marker:
|
||||
start = all_arns.index(marker) + 1
|
||||
else:
|
||||
start = 0
|
||||
rules_resp = all_rules[start:start + page_size]
|
||||
next_marker = None
|
||||
|
||||
if len(all_rules) > start + page_size:
|
||||
next_marker = rules_resp[-1].arn
|
||||
template = self.response_template(DESCRIBE_RULES_TEMPLATE)
|
||||
return template.render(rules=rules_resp, marker=next_marker)
|
||||
|
||||
def describe_target_groups(self):
|
||||
load_balancer_arn = self._get_param('LoadBalancerArn')
|
||||
target_group_arns = self._get_multi_param('TargetGroupArns.member')
|
||||
@ -133,6 +177,12 @@ class ELBV2Response(BaseResponse):
|
||||
template = self.response_template(DELETE_LOAD_BALANCER_TEMPLATE)
|
||||
return template.render()
|
||||
|
||||
def delete_rule(self):
|
||||
arn = self._get_param('RuleArn')
|
||||
self.elbv2_backend.delete_rule(arn)
|
||||
template = self.response_template(DELETE_RULE_TEMPLATE)
|
||||
return template.render()
|
||||
|
||||
def delete_target_group(self):
|
||||
arn = self._get_param('TargetGroupArn')
|
||||
self.elbv2_backend.delete_target_group(arn)
|
||||
@ -145,6 +195,28 @@ class ELBV2Response(BaseResponse):
|
||||
template = self.response_template(DELETE_LISTENER_TEMPLATE)
|
||||
return template.render()
|
||||
|
||||
def modify_rule(self):
|
||||
rule_arn = self._get_param('RuleArn')
|
||||
_conditions = self._get_list_prefix('Conditions.member')
|
||||
conditions = []
|
||||
for _condition in _conditions:
|
||||
condition = {}
|
||||
condition['field'] = _condition['field']
|
||||
values = sorted(
|
||||
[e for e in _condition.items() if e[0].startswith('values.member')],
|
||||
key=lambda x: x[0]
|
||||
)
|
||||
condition['values'] = [e[1] for e in values]
|
||||
conditions.append(condition)
|
||||
actions = self._get_list_prefix('Actions.member')
|
||||
rules = self.elbv2_backend.modify_rule(
|
||||
rule_arn=rule_arn,
|
||||
conditions=conditions,
|
||||
actions=actions
|
||||
)
|
||||
template = self.response_template(MODIFY_RULE_TEMPLATE)
|
||||
return template.render(rules=rules)
|
||||
|
||||
def modify_target_group_attributes(self):
|
||||
target_group_arn = self._get_param('TargetGroupArn')
|
||||
target_group = self.elbv2_backend.target_groups.get(target_group_arn)
|
||||
@ -182,6 +254,14 @@ class ELBV2Response(BaseResponse):
|
||||
template = self.response_template(DESCRIBE_TARGET_HEALTH_TEMPLATE)
|
||||
return template.render(target_health_descriptions=target_health_descriptions)
|
||||
|
||||
def set_rule_priorities(self):
|
||||
rule_priorities = self._get_list_prefix('RulePriorities.member')
|
||||
for rule_priority in rule_priorities:
|
||||
rule_priority['priority'] = int(rule_priority['priority'])
|
||||
rules = self.elbv2_backend.set_rule_priorities(rule_priorities)
|
||||
template = self.response_template(SET_RULE_PRIORITIES_TEMPLATE)
|
||||
return template.render(rules=rules)
|
||||
|
||||
def add_tags(self):
|
||||
resource_arns = self._get_multi_param('ResourceArns.member')
|
||||
|
||||
@ -321,6 +401,43 @@ CREATE_LOAD_BALANCER_TEMPLATE = """<CreateLoadBalancerResponse xmlns="http://ela
|
||||
</ResponseMetadata>
|
||||
</CreateLoadBalancerResponse>"""
|
||||
|
||||
CREATE_RULE_TEMPLATE = """<CreateRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<CreateRuleResult>
|
||||
<Rules>
|
||||
{% for rule in rules %}
|
||||
<member>
|
||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
||||
<Conditions>
|
||||
{% for condition in rule.conditions %}
|
||||
<member>
|
||||
<Field>{{ condition["field"] }}</Field>
|
||||
<Values>
|
||||
{% for value in condition["values"] %}
|
||||
<member>{{ value }}</member>
|
||||
{% endfor %}
|
||||
</Values>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Conditions>
|
||||
<Priority>{{ rule.priority }}</Priority>
|
||||
<Actions>
|
||||
{% for action in rule.actions %}
|
||||
<member>
|
||||
<Type>{{ action["type"] }}</Type>
|
||||
<TargetGroupArn>{{ action["target_group_arn"] }}</TargetGroupArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Actions>
|
||||
<RuleArn>{{ rule.arn }}</RuleArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Rules>
|
||||
</CreateRuleResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>c5478c83-f397-11e5-bb98-57195a6eb84a</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateRuleResponse>"""
|
||||
|
||||
CREATE_TARGET_GROUP_TEMPLATE = """<CreateTargetGroupResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<CreateTargetGroupResult>
|
||||
<TargetGroups>
|
||||
@ -387,6 +504,13 @@ DELETE_LOAD_BALANCER_TEMPLATE = """<DeleteLoadBalancerResponse xmlns="http://ela
|
||||
</ResponseMetadata>
|
||||
</DeleteLoadBalancerResponse>"""
|
||||
|
||||
DELETE_RULE_TEMPLATE = """<DeleteRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<DeleteRuleResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteRuleResponse>"""
|
||||
|
||||
DELETE_TARGET_GROUP_TEMPLATE = """<DeleteTargetGroupResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<DeleteTargetGroupResult/>
|
||||
<ResponseMetadata>
|
||||
@ -442,6 +566,45 @@ DESCRIBE_LOAD_BALANCERS_TEMPLATE = """<DescribeLoadBalancersResponse xmlns="http
|
||||
</ResponseMetadata>
|
||||
</DescribeLoadBalancersResponse>"""
|
||||
|
||||
DESCRIBE_RULES_TEMPLATE = """<DescribeRulesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<DescribeRulesResult>
|
||||
<Rules>
|
||||
{% for rule in rules %}
|
||||
<member>
|
||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
||||
<Conditions>
|
||||
{% for condition in rule.conditions %}
|
||||
<member>
|
||||
<Field>{{ condition["field"] }}</Field>
|
||||
<Values>
|
||||
{% for value in condition["values"] %}
|
||||
<member>{{ value }}</member>
|
||||
{% endfor %}
|
||||
</Values>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Conditions>
|
||||
<Priority>{{ rule.priority }}</Priority>
|
||||
<Actions>
|
||||
{% for action in rule.actions %}
|
||||
<member>
|
||||
<Type>{{ action["type"] }}</Type>
|
||||
<TargetGroupArn>{{ action["target_group_arn"] }}</TargetGroupArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Actions>
|
||||
<RuleArn>{{ rule.arn }}</RuleArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Rules>
|
||||
{% if marker %}
|
||||
<NextMarker>{{ marker }}</NextMarker>
|
||||
{% endif %}
|
||||
</DescribeRulesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>74926cf3-f3a3-11e5-b543-9f2c3fbb9bee</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeRulesResponse>"""
|
||||
|
||||
DESCRIBE_TARGET_GROUPS_TEMPLATE = """<DescribeTargetGroupsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<DescribeTargetGroupsResult>
|
||||
@ -544,6 +707,43 @@ CONFIGURE_HEALTH_CHECK_TEMPLATE = """<ConfigureHealthCheckResponse xmlns="http:/
|
||||
</ResponseMetadata>
|
||||
</ConfigureHealthCheckResponse>"""
|
||||
|
||||
MODIFY_RULE_TEMPLATE = """<ModifyRuleResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<ModifyRuleResult>
|
||||
<Rules>
|
||||
{% for rule in rules %}
|
||||
<member>
|
||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
||||
<Conditions>
|
||||
{% for condition in rule.conditions %}
|
||||
<member>
|
||||
<Field>{{ condition["field"] }}</Field>
|
||||
<Values>
|
||||
{% for value in condition["values"] %}
|
||||
<member>{{ value }}</member>
|
||||
{% endfor %}
|
||||
</Values>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Conditions>
|
||||
<Priority>{{ rule.priority }}</Priority>
|
||||
<Actions>
|
||||
{% for action in rule.actions %}
|
||||
<member>
|
||||
<Type>{{ action["type"] }}</Type>
|
||||
<TargetGroupArn>{{ action["target_group_arn"] }}</TargetGroupArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Actions>
|
||||
<RuleArn>{{ rule.arn }}</RuleArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Rules>
|
||||
</ModifyRuleResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>c5478c83-f397-11e5-bb98-57195a6eb84a</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ModifyRuleResponse>"""
|
||||
|
||||
MODIFY_TARGET_GROUP_ATTRIBUTES_TEMPLATE = """<ModifyTargetGroupAttributesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<ModifyTargetGroupAttributesResult>
|
||||
<Attributes>
|
||||
@ -703,3 +903,40 @@ DESCRIBE_TARGET_HEALTH_TEMPLATE = """<DescribeTargetHealthResponse xmlns="http:/
|
||||
<RequestId>c534f810-f389-11e5-9192-3fff33344cfa</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeTargetHealthResponse>"""
|
||||
|
||||
SET_RULE_PRIORITIES_TEMPLATE = """<SetRulePrioritiesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||
<SetRulePrioritiesResult>
|
||||
<Rules>
|
||||
{% for rule in rules %}
|
||||
<member>
|
||||
<IsDefault>{{ "true" if rule.is_default else "false" }}</IsDefault>
|
||||
<Conditions>
|
||||
{% for condition in rule.conditions %}
|
||||
<member>
|
||||
<Field>{{ condition["field"] }}</Field>
|
||||
<Values>
|
||||
{% for value in condition["values"] %}
|
||||
<member>{{ value }}</member>
|
||||
{% endfor %}
|
||||
</Values>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Conditions>
|
||||
<Priority>{{ rule.priority }}</Priority>
|
||||
<Actions>
|
||||
{% for action in rule.actions %}
|
||||
<member>
|
||||
<Type>{{ action["type"] }}</Type>
|
||||
<TargetGroupArn>{{ action["target_group_arn"] }}</TargetGroupArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Actions>
|
||||
<RuleArn>{{ rule.arn }}</RuleArn>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Rules>
|
||||
</SetRulePrioritiesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4d7a8036-f3a7-11e5-9c02-8fd20490d5a6</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SetRulePrioritiesResponse>"""
|
||||
|
@ -518,3 +518,279 @@ def test_target_group_attributes():
|
||||
attributes = {attr['Key']: attr['Value'] for attr in response['Attributes']}
|
||||
attributes['stickiness.type'].should.equal('lb_cookie')
|
||||
attributes['stickiness.enabled'].should.equal('true')
|
||||
|
||||
|
||||
@mock_elbv2
|
||||
@mock_ec2
|
||||
def test_handle_listener_rules():
|
||||
conn = boto3.client('elbv2', region_name='us-east-1')
|
||||
ec2 = boto3.resource('ec2', region_name='us-east-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='us-east-1a')
|
||||
subnet2 = ec2.create_subnet(VpcId=vpc.id, CidrBlock='172.28.7.192/26', AvailabilityZone='us-east-1b')
|
||||
|
||||
response = conn.create_load_balancer(
|
||||
Name='my-lb',
|
||||
Subnets=[subnet1.id, subnet2.id],
|
||||
SecurityGroups=[security_group.id],
|
||||
Scheme='internal',
|
||||
Tags=[{'Key': 'key_name', 'Value': 'a_value'}])
|
||||
|
||||
load_balancer_arn = response.get('LoadBalancers')[0].get('LoadBalancerArn')
|
||||
|
||||
response = conn.create_target_group(
|
||||
Name='a-target',
|
||||
Protocol='HTTP',
|
||||
Port=8080,
|
||||
VpcId=vpc.id,
|
||||
HealthCheckProtocol='HTTP',
|
||||
HealthCheckPort='8080',
|
||||
HealthCheckPath='/',
|
||||
HealthCheckIntervalSeconds=5,
|
||||
HealthCheckTimeoutSeconds=5,
|
||||
HealthyThresholdCount=5,
|
||||
UnhealthyThresholdCount=2,
|
||||
Matcher={'HttpCode': '200'})
|
||||
target_group = response.get('TargetGroups')[0]
|
||||
|
||||
# Plain HTTP listener
|
||||
response = conn.create_listener(
|
||||
LoadBalancerArn=load_balancer_arn,
|
||||
Protocol='HTTP',
|
||||
Port=80,
|
||||
DefaultActions=[{'Type': 'forward', 'TargetGroupArn': target_group.get('TargetGroupArn')}])
|
||||
listener = response.get('Listeners')[0]
|
||||
listener.get('Port').should.equal(80)
|
||||
listener.get('Protocol').should.equal('HTTP')
|
||||
listener.get('DefaultActions').should.equal([{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'}])
|
||||
http_listener_arn = listener.get('ListenerArn')
|
||||
|
||||
# create first rule
|
||||
priority = 100
|
||||
host = 'xxx.example.com'
|
||||
path_pattern = 'foobar'
|
||||
created_rule = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [ host ]
|
||||
},
|
||||
{
|
||||
'Field': 'path-pattern',
|
||||
'Values': [ path_pattern ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)['Rules'][0]
|
||||
created_rule['Priority'].should.equal('100')
|
||||
|
||||
# check if rules is sorted by priority
|
||||
priority = 50
|
||||
host = 'yyy.example.com'
|
||||
path_pattern = 'foobar'
|
||||
rules = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [ host ]
|
||||
},
|
||||
{
|
||||
'Field': 'path-pattern',
|
||||
'Values': [ path_pattern ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)
|
||||
|
||||
# test for PriorityInUse
|
||||
host2 = 'yyy.example.com'
|
||||
with assert_raises(ClientError):
|
||||
r = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [ host ]
|
||||
},
|
||||
{
|
||||
'Field': 'path-pattern',
|
||||
'Values': [ path_pattern ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)
|
||||
|
||||
|
||||
# test for describe listeners
|
||||
obtained_rules = conn.describe_rules(ListenerArn=http_listener_arn)
|
||||
len(obtained_rules['Rules']).should.equal(3)
|
||||
priorities = [rule['Priority'] for rule in obtained_rules['Rules']]
|
||||
priorities.should.equal(['50', '100', 'default'])
|
||||
|
||||
first_rule = obtained_rules['Rules'][0]
|
||||
second_rule = obtained_rules['Rules'][1]
|
||||
obtained_rules = conn.describe_rules(RuleArns=[first_rule['RuleArn']])
|
||||
obtained_rules['Rules'].should.equal([first_rule])
|
||||
|
||||
# test for pagination
|
||||
obtained_rules = conn.describe_rules(ListenerArn=http_listener_arn, PageSize=1)
|
||||
len(obtained_rules['Rules']).should.equal(1)
|
||||
obtained_rules.should.have.key('NextMarker')
|
||||
next_marker = obtained_rules['NextMarker']
|
||||
|
||||
following_rules = conn.describe_rules(ListenerArn=http_listener_arn, PageSize=1, Marker=next_marker)
|
||||
len(following_rules['Rules']).should.equal(1)
|
||||
following_rules.should.have.key('NextMarker')
|
||||
following_rules['Rules'][0]['RuleArn'].should_not.equal(obtained_rules['Rules'][0]['RuleArn'])
|
||||
|
||||
# test for invalid describe rule request
|
||||
with assert_raises(ClientError):
|
||||
conn.describe_rules()
|
||||
with assert_raises(ClientError):
|
||||
conn.describe_rules(RuleArns=[])
|
||||
with assert_raises(ClientError):
|
||||
conn.describe_rules(
|
||||
ListenerArn=http_listener_arn,
|
||||
RuleArns=[first_rule['RuleArn']]
|
||||
)
|
||||
|
||||
# modify rule
|
||||
new_host = 'new.example.com'
|
||||
new_path_pattern = 'new_path'
|
||||
modified_rule = conn.modify_rule(
|
||||
RuleArn=first_rule['RuleArn'],
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [ new_host ]
|
||||
},
|
||||
{
|
||||
'Field': 'path-pattern',
|
||||
'Values': [ new_path_pattern ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
|
||||
)['Rules'][0]
|
||||
rules = conn.describe_rules(ListenerArn=http_listener_arn)
|
||||
modified_rule.should.equal(rules['Rules'][0])
|
||||
|
||||
# modify priority
|
||||
conn.set_rule_priorities(
|
||||
RulePriorities=[
|
||||
{'RuleArn': first_rule['RuleArn'], 'Priority': int(first_rule['Priority']) - 1}
|
||||
]
|
||||
)
|
||||
with assert_raises(ClientError):
|
||||
conn.set_rule_priorities(
|
||||
RulePriorities=[
|
||||
{'RuleArn': first_rule['RuleArn'], 'Priority': 999},
|
||||
{'RuleArn': second_rule['RuleArn'], 'Priority': 999}
|
||||
]
|
||||
)
|
||||
|
||||
# delete
|
||||
arn = first_rule['RuleArn']
|
||||
conn.delete_rule(RuleArn=arn)
|
||||
|
||||
# test for invalid action type
|
||||
safe_priority = 2
|
||||
with assert_raises(ClientError):
|
||||
r = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=safe_priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [ host ]
|
||||
},
|
||||
{
|
||||
'Field': 'path-pattern',
|
||||
'Values': [ path_pattern ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward2'
|
||||
}]
|
||||
)
|
||||
|
||||
# test for invalid action type
|
||||
safe_priority = 2
|
||||
invalid_target_group_arn = target_group.get('TargetGroupArn') + 'x'
|
||||
with assert_raises(ClientError):
|
||||
r = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=safe_priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [ host ]
|
||||
},
|
||||
{
|
||||
'Field': 'path-pattern',
|
||||
'Values': [ path_pattern ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': invalid_target_group_arn,
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)
|
||||
|
||||
# test for invalid condition field_name
|
||||
safe_priority = 2
|
||||
with assert_raises(ClientError):
|
||||
r = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=safe_priority,
|
||||
Conditions=[{
|
||||
'Field': 'xxxxxxx',
|
||||
'Values': [ host ]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)
|
||||
|
||||
# test for emptry condition value
|
||||
safe_priority = 2
|
||||
with assert_raises(ClientError):
|
||||
r = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=safe_priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': []
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)
|
||||
|
||||
# test for multiple condition value
|
||||
safe_priority = 2
|
||||
with assert_raises(ClientError):
|
||||
r = conn.create_rule(
|
||||
ListenerArn=http_listener_arn,
|
||||
Priority=safe_priority,
|
||||
Conditions=[{
|
||||
'Field': 'host-header',
|
||||
'Values': [host, host]
|
||||
}],
|
||||
Actions=[{
|
||||
'TargetGroupArn': target_group.get('TargetGroupArn'),
|
||||
'Type': 'forward'
|
||||
}]
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user