From bbb021d06d06424823f315930d155a8b11ab9524 Mon Sep 17 00:00:00 2001 From: Andrew Harris Date: Tue, 14 Jul 2015 23:54:58 +0000 Subject: [PATCH] implement mocking for lb policies --- moto/elb/models.py | 63 ++++++++++++- moto/elb/responses.py | 179 +++++++++++++++++++++++++++++++++++-- tests/test_elb/test_elb.py | 107 ++++++++++++++++++++++ 3 files changed, 340 insertions(+), 9 deletions(-) diff --git a/moto/elb/models.py b/moto/elb/models.py index e1487f5aa..2a9764a40 100644 --- a/moto/elb/models.py +++ b/moto/elb/models.py @@ -8,6 +8,12 @@ from boto.ec2.elb.attributes import ( AccessLogAttribute, CrossZoneLoadBalancingAttribute, ) +from boto.ec2.elb.policies import ( + Policies, + AppCookieStickinessPolicy, + LBCookieStickinessPolicy, + OtherPolicy, +) from moto.core import BaseBackend @@ -27,6 +33,19 @@ class FakeListener(object): self.instance_port = instance_port self.protocol = protocol.upper() self.ssl_certificate_id = ssl_certificate_id + self.policy_names = [] + + def __repr__(self): + return "FakeListener(lbp: %s, inp: %s, pro: %s, cid: %s, policies: %s)" % (self.load_balancer_port, self.instance_port, self.protocol, self.ssl_certificate_id, self.policy_names) + + +class FakeBackend(object): + def __init__(self, instance_port): + self.instance_port = instance_port + self.policy_names = [] + + def __repr__(self): + return "FakeBackend(inp: %s, policies: %s)" % (self.instance_port, self.policy_names) class FakeLoadBalancer(object): @@ -36,7 +55,12 @@ class FakeLoadBalancer(object): self.instance_ids = [] self.zones = zones self.listeners = [] + self.backends = [] self.attributes = FakeLoadBalancer.get_default_attributes() + self.policies = Policies() + self.policies.other_policies = [] + self.policies.app_cookie_stickiness_policies = [] + self.policies.lb_cookie_stickiness_policies = [] for protocol, lb_port, instance_port, ssl_certificate_id in ports: listener = FakeListener( @@ -46,6 +70,13 @@ class FakeLoadBalancer(object): ssl_certificate_id=ssl_certificate_id ) self.listeners.append(listener) + + # it is unclear per the AWS documentation as to when or how backend + # information gets set, so let's guess and set it here *shrug* + backend = FakeBackend( + instance_port = instance_port, + ) + self.backends.append(backend) @classmethod def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): @@ -103,7 +134,6 @@ class FakeLoadBalancer(object): return attributes - class ELBBackend(BaseBackend): def __init__(self): @@ -201,6 +231,37 @@ class ELBBackend(BaseBackend): load_balancer.attributes.connecting_settings = attribute return load_balancer + def create_lb_other_policy(self, load_balancer_name, other_policy): + load_balancer = self.get_load_balancer(load_balancer_name) + load_balancer.policies.other_policies.append(other_policy) + return load_balancer + + def create_app_cookie_stickiness_policy(self, load_balancer_name, policy): + load_balancer = self.get_load_balancer(load_balancer_name) + load_balancer.policies.app_cookie_stickiness_policies.append(policy) + return load_balancer + + def create_lb_cookie_stickiness_policy(self, load_balancer_name, policy): + load_balancer = self.get_load_balancer(load_balancer_name) + load_balancer.policies.lb_cookie_stickiness_policies.append(policy) + return load_balancer + + def set_load_balancer_policies_of_backend_server(self, load_balancer_name, instance_port, policies): + load_balancer = self.get_load_balancer(load_balancer_name) + backend = [b for b in load_balancer.backends if int(b.instance_port) == instance_port][0] + backend_idx = load_balancer.backends.index(backend) + backend.policy_names = policies + load_balancer.backends[backend_idx] = backend + return load_balancer + + def set_load_balancer_policies_of_listener(self, load_balancer_name, load_balancer_port, policies): + load_balancer = self.get_load_balancer(load_balancer_name) + listener = [l for l in load_balancer.listeners if int(l.load_balancer_port) == load_balancer_port][0] + listener_idx = load_balancer.listeners.index(listener) + listener.policy_names = policies + load_balancer.listeners[listener_idx] = listener + return load_balancer + elb_backends = {} for region in boto.ec2.elb.regions(): diff --git a/moto/elb/responses.py b/moto/elb/responses.py index d33a78fc8..d36457508 100644 --- a/moto/elb/responses.py +++ b/moto/elb/responses.py @@ -5,6 +5,12 @@ from boto.ec2.elb.attributes import ( AccessLogAttribute, CrossZoneLoadBalancingAttribute, ) +from boto.ec2.elb.policies import ( + Policies, + AppCookieStickinessPolicy, + LBCookieStickinessPolicy, + OtherPolicy, +) from moto.core.responses import BaseResponse from .models import elb_backends @@ -177,6 +183,101 @@ class ELBResponse(BaseResponse): template = self.response_template(MODIFY_ATTRIBUTES_TEMPLATE) return template.render(attributes=load_balancer.attributes) + def create_load_balancer_policy(self): + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + load_balancer = self.elb_backend.describe_load_balancers(load_balancer_name)[0] + + other_policy = OtherPolicy() + policy_name = [value[0] for key, value in self.querystring.items() if "PolicyName" in key][0] + other_policy.policy_name = policy_name + + self.elb_backend.create_lb_other_policy(load_balancer_name, other_policy) + + template = self.response_template(CREATE_LOAD_BALANCER_POLICY_TEMPLATE) + return template.render() + + def create_app_cookie_stickiness_policy(self): + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + load_balancer = self.elb_backend.describe_load_balancers(load_balancer_name)[0] + + policy = AppCookieStickinessPolicy() + policy_name = [value[0] for key, value in self.querystring.items() if "PolicyName" in key][0] + policy.policy_name = policy_name + cookie_name = [value[0] for key, value in self.querystring.items() if "CookieName" in key][0] + policy.cookie_name = cookie_name + + self.elb_backend.create_app_cookie_stickiness_policy(load_balancer_name, policy) + + template = self.response_template(CREATE_LOAD_BALANCER_POLICY_TEMPLATE) + return template.render() + + def create_lbcookie_stickiness_policy(self): + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + load_balancer = self.elb_backend.describe_load_balancers(load_balancer_name)[0] + + policy = AppCookieStickinessPolicy() + policy_name = [value[0] for key, value in self.querystring.items() if "PolicyName" in key][0] + policy.policy_name = policy_name + cookie_expirations = [value[0] for key, value in self.querystring.items() if "CookieExpirationPeriod" in key] + if cookie_expirations: + policy.cookie_expiration_period = long(cookie_expirations[0]) + else: + policy.cookie_expiration_period = None + + self.elb_backend.create_lb_cookie_stickiness_policy(load_balancer_name, policy) + + template = self.response_template(CREATE_LOAD_BALANCER_POLICY_TEMPLATE) + return template.render() + + def set_load_balancer_policies_of_listener(self): + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + load_balancer = self.elb_backend.describe_load_balancers(load_balancer_name)[0] + load_balancer_port = int(self.querystring.get('LoadBalancerPort')[0]) + + mb_listener = [l for l in load_balancer.listeners if int(l.load_balancer_port) == load_balancer_port] + if mb_listener: + policies = [] + policy_index = 1 + while True: + try: + policy = self.querystring['PolicyNames.member.{0}'.format(policy_index)][0] + except KeyError: + break + + policy_index += 1 + policies.append(str(policy)) + + self.elb_backend.set_load_balancer_policies_of_listener(load_balancer_name, load_balancer_port, policies) + # else: explode? + + template = self.response_template(SET_LOAD_BALANCER_POLICIES_OF_LISTENER_TEMPLATE) + return template.render() + + def set_load_balancer_policies_for_backend_server(self): + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + load_balancer = self.elb_backend.describe_load_balancers(load_balancer_name)[0] + instance_port = int(self.querystring.get('InstancePort')[0]) + + mb_backend = [b for b in load_balancer.backends if int(b.instance_port) == instance_port] + if mb_backend: + policies = [] + policy_index = 1 + while True: + try: + policy = self.querystring['PolicyNames.member.{0}'.format(policy_index)][0] + except KeyError: + break + + policy_index += 1 + policies.append(str(policy)) + + self.elb_backend.set_load_balancer_policies_of_backend_server(load_balancer_name, instance_port, policies) + + # else: explode? + + template = self.response_template(SET_LOAD_BALANCER_POLICIES_FOR_BACKEND_SERVER_TEMPLATE) + return template.render() + def describe_instance_health(self): load_balancer_name = self.querystring.get('LoadBalancerName')[0] instance_ids = [value[0] for key, value in self.querystring.items() if "Instances.member" in key] @@ -223,7 +324,9 @@ DESCRIBE_LOAD_BALANCERS_TEMPLATE = """ + + + 83c88b9d-12b7-11e3-8b82-87b12EXAMPLE + + +""" + +SET_LOAD_BALANCER_POLICIES_OF_LISTENER_TEMPLATE = """ + + + 07b1ecbc-1100-11e3-acaf-dd7edEXAMPLE + + +""" + +SET_LOAD_BALANCER_POLICIES_FOR_BACKEND_SERVER_TEMPLATE = """ + + + 0eb9b381-dde0-11e2-8d78-6ddbaEXAMPLE + + +""" + DESCRIBE_INSTANCE_HEALTH_TEMPLATE = """ diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py index 9bb0c136b..1e80e5b53 100644 --- a/tests/test_elb/test_elb.py +++ b/tests/test_elb/test_elb.py @@ -7,6 +7,12 @@ from boto.ec2.elb.attributes import ( ConnectionDrainingAttribute, AccessLogAttribute, ) +from boto.ec2.elb.policies import ( + Policies, + AppCookieStickinessPolicy, + LBCookieStickinessPolicy, + OtherPolicy, +) import sure # noqa from moto import mock_elb, mock_ec2 @@ -315,6 +321,107 @@ def test_connection_settings_attribute(): attributes = lb.get_attributes(force=True) attributes.connecting_settings.idle_timeout.should.equal(60) +@mock_elb +def test_create_lb_cookie_stickiness_policy(): + conn = boto.connect_elb() + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', [], ports) + cookie_expiration_period = 60 + policy_name = "LBCookieStickinessPolicy" + + lb.create_cookie_stickiness_policy(cookie_expiration_period, policy_name) + + lb = conn.get_all_load_balancers()[0] + # There appears to be a quirk about boto, whereby it returns a unicode + # string for cookie_expiration_period, despite being stated in + # documentation to be a long. + # + # To work around that, this value is converted to a long and checked. + cookie_expiration_period_response_str = lb.policies.lb_cookie_stickiness_policies[0].cookie_expiration_period + long(cookie_expiration_period_response_str).should.equal(cookie_expiration_period) + lb.policies.lb_cookie_stickiness_policies[0].policy_name.should.equal(policy_name) + +@mock_elb +def test_create_lb_cookie_stickiness_policy_no_expiry(): + conn = boto.connect_elb() + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', [], ports) + policy_name = "LBCookieStickinessPolicy" + + lb.create_cookie_stickiness_policy(None, policy_name) + + lb = conn.get_all_load_balancers()[0] + lb.policies.lb_cookie_stickiness_policies[0].cookie_expiration_period.should.be.none + lb.policies.lb_cookie_stickiness_policies[0].policy_name.should.equal(policy_name) + +@mock_elb +def test_create_app_cookie_stickiness_policy(): + conn = boto.connect_elb() + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', [], ports) + cookie_name = "my-stickiness-policy" + policy_name = "AppCookieStickinessPolicy" + + lb.create_app_cookie_stickiness_policy(cookie_name, policy_name) + + lb = conn.get_all_load_balancers()[0] + lb.policies.app_cookie_stickiness_policies[0].cookie_name.should.equal(cookie_name) + lb.policies.app_cookie_stickiness_policies[0].policy_name.should.equal(policy_name) + +@mock_elb +def test_create_lb_policy(): + conn = boto.connect_elb() + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', [], ports) + policy_name = "ProxyPolicy" + + lb.create_lb_policy(policy_name, 'ProxyProtocolPolicyType', {'ProxyProtocol': True}) + + lb = conn.get_all_load_balancers()[0] + lb.policies.other_policies[0].policy_name.should.equal(policy_name) + +@mock_elb +def test_set_policies_of_listener(): + conn = boto.connect_elb() + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', [], ports) + listener_port = 80 + policy_name = "my-stickiness-policy" + + # boto docs currently state that zero or one policy may be associated + # with a given listener + + # in a real flow, it is necessary first to create a policy, + # then to set that policy to the listener + lb.create_cookie_stickiness_policy(None, policy_name) + lb.set_policies_of_listener(listener_port, [policy_name]) + + lb = conn.get_all_load_balancers()[0] + print lb.listeners + print lb.backends + listener = lb.listeners[0] + listener.load_balancer_port.should.equal(listener_port) + # by contrast to a backend, a listener stores only policy name strings + listener.policy_names[0].should.equal(policy_name) + +@mock_elb +def test_set_policies_of_backend_server(): + conn = boto.connect_elb() + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', [], ports) + instance_port = 8080 + policy_name = "ProxyPolicy" + + # in a real flow, it is necessary first to create a policy, + # then to set that policy to the backend + lb.create_lb_policy(policy_name, 'ProxyProtocolPolicyType', {'ProxyProtocol': True}) + lb.set_policies_of_backend_server(instance_port, [policy_name]) + + lb = conn.get_all_load_balancers()[0] + backend = lb.backends[0] + backend.instance_port.should.equal(instance_port) + # by contrast to a listener, a backend stores OtherPolicy objects + backend.policies[0].policy_name.should.equal(policy_name) @mock_ec2 @mock_elb