add create_rule to elbv2
This commit is contained in:
parent
b4013f0e60
commit
05a2715f4b
@ -101,3 +101,44 @@ class EmptyListenersError(ELBClientError):
|
|||||||
super(EmptyListenersError, self).__init__(
|
super(EmptyListenersError, self).__init__(
|
||||||
"ValidationError",
|
"ValidationError",
|
||||||
"Listeners cannot be empty")
|
"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
|
||||||
|
)
|
||||||
|
@ -14,6 +14,11 @@ from .exceptions import (
|
|||||||
SubnetNotFoundError,
|
SubnetNotFoundError,
|
||||||
TargetGroupNotFoundError,
|
TargetGroupNotFoundError,
|
||||||
TooManyTagsError,
|
TooManyTagsError,
|
||||||
|
PriorityInUseError,
|
||||||
|
InvalidConditionFieldError,
|
||||||
|
InvalidConditionValueError,
|
||||||
|
InvalidActionTypeError,
|
||||||
|
ActionTargetGroupNotFoundError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +97,34 @@ class FakeListener(BaseModel):
|
|||||||
self.ssl_policy = ssl_policy
|
self.ssl_policy = ssl_policy
|
||||||
self.certificate = certificate
|
self.certificate = certificate
|
||||||
self.default_actions = default_actions
|
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):
|
class FakeBackend(BaseModel):
|
||||||
@ -181,6 +214,53 @@ class ELBv2Backend(BaseBackend):
|
|||||||
self.load_balancers[arn] = new_load_balancer
|
self.load_balancers[arn] = new_load_balancer
|
||||||
return 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 ListenerNotFound()
|
||||||
|
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 listener.rules
|
||||||
|
|
||||||
def create_target_group(self, name, **kwargs):
|
def create_target_group(self, name, **kwargs):
|
||||||
for target_group in self.target_groups.values():
|
for target_group in self.target_groups.values():
|
||||||
if target_group.name == name:
|
if target_group.name == name:
|
||||||
|
@ -28,6 +28,30 @@ class ELBV2Response(BaseResponse):
|
|||||||
template = self.response_template(CREATE_LOAD_BALANCER_TEMPLATE)
|
template = self.response_template(CREATE_LOAD_BALANCER_TEMPLATE)
|
||||||
return template.render(load_balancer=load_balancer)
|
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):
|
def create_target_group(self):
|
||||||
name = self._get_param('Name')
|
name = self._get_param('Name')
|
||||||
vpc_id = self._get_param('VpcId')
|
vpc_id = self._get_param('VpcId')
|
||||||
@ -321,6 +345,43 @@ CREATE_LOAD_BALANCER_TEMPLATE = """<CreateLoadBalancerResponse xmlns="http://ela
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</CreateLoadBalancerResponse>"""
|
</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/">
|
CREATE_TARGET_GROUP_TEMPLATE = """<CreateTargetGroupResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2015-12-01/">
|
||||||
<CreateTargetGroupResult>
|
<CreateTargetGroupResult>
|
||||||
<TargetGroups>
|
<TargetGroups>
|
||||||
|
@ -518,3 +518,207 @@ def test_target_group_attributes():
|
|||||||
attributes = {attr['Key']: attr['Value'] for attr in response['Attributes']}
|
attributes = {attr['Key']: attr['Value'] for attr in response['Attributes']}
|
||||||
attributes['stickiness.type'].should.equal('lb_cookie')
|
attributes['stickiness.type'].should.equal('lb_cookie')
|
||||||
attributes['stickiness.enabled'].should.equal('true')
|
attributes['stickiness.enabled'].should.equal('true')
|
||||||
|
|
||||||
|
|
||||||
|
@mock_elbv2
|
||||||
|
@mock_ec2
|
||||||
|
def test_create_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'
|
||||||
|
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'
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
rules['Rules'][0].get('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'
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
priorities = [rule['Priority'] for rule in rules['Rules']]
|
||||||
|
priorities.should.equal(['50', '100', 'default'])
|
||||||
|
|
||||||
|
# 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 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 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…
Reference in New Issue
Block a user