diff --git a/moto/__init__.py b/moto/__init__.py index 49f121a3c..302156efe 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -3,6 +3,7 @@ logging.getLogger('boto').setLevel(logging.CRITICAL) from .dynamodb import mock_dynamodb from .ec2 import mock_ec2 +from .elb import mock_elb from .s3 import mock_s3 from .ses import mock_ses from .sqs import mock_sqs diff --git a/moto/elb/__init__.py b/moto/elb/__init__.py new file mode 100644 index 000000000..fcadac99e --- /dev/null +++ b/moto/elb/__init__.py @@ -0,0 +1,2 @@ +from .models import elb_backend +mock_elb = elb_backend.decorator diff --git a/moto/elb/models.py b/moto/elb/models.py new file mode 100644 index 000000000..aff7f082b --- /dev/null +++ b/moto/elb/models.py @@ -0,0 +1,80 @@ +from moto.core import BaseBackend + + +class FakeHealthCheck(object): + def __init__(self, timeout, healthy_threshold, unhealthy_threshold, + interval, target): + self.timeout = timeout + self.healthy_threshold = healthy_threshold + self.unhealthy_threshold = unhealthy_threshold + self.interval = interval + self.target = target + + +class FakeListener(object): + def __init__(self, load_balancer_port, instance_port, protocol): + self.load_balancer_port = load_balancer_port + self.instance_port = instance_port + self.protocol = protocol.upper() + + +class FakeLoadBalancer(object): + def __init__(self, name, zones, ports): + self.name = name + self.health_check = None + self.instance_ids = [] + self.zones = zones + self.listeners = [] + for protocol, lb_port, instance_port in ports: + listener = FakeListener( + protocol=protocol, + load_balancer_port=lb_port, + instance_port=instance_port, + ) + self.listeners.append(listener) + + +class ELBBackend(BaseBackend): + + def __init__(self): + self.load_balancers = {} + + def create_load_balancer(self, name, zones, ports): + new_load_balancer = FakeLoadBalancer(name=name, zones=zones, ports=ports) + self.load_balancers[name] = new_load_balancer + return new_load_balancer + + def describe_load_balancers(self, names): + balancers = self.load_balancers.values() + if names: + return [balancer for balancer in balancers if balancer.name in names] + else: + return balancers + + def delete_load_balancer(self, load_balancer_name): + self.load_balancers.pop(load_balancer_name, None) + + def get_load_balancer(self, load_balancer_name): + return self.load_balancers.get(load_balancer_name) + + def configure_health_check(self, load_balancer_name, timeout, + healthy_threshold, unhealthy_threshold, interval, + target): + check = FakeHealthCheck(timeout, healthy_threshold, unhealthy_threshold, + interval, target) + load_balancer = self.get_load_balancer(load_balancer_name) + load_balancer.health_check = check + return check + + def register_instances(self, load_balancer_name, instance_ids): + load_balancer = self.get_load_balancer(load_balancer_name) + load_balancer.instance_ids.extend(instance_ids) + return load_balancer + + def deregister_instances(self, load_balancer_name, instance_ids): + load_balancer = self.get_load_balancer(load_balancer_name) + new_instance_ids = [instance_id for instance_id in load_balancer.instance_ids if instance_id not in instance_ids] + load_balancer.instance_ids = new_instance_ids + return load_balancer + +elb_backend = ELBBackend() diff --git a/moto/elb/responses.py b/moto/elb/responses.py new file mode 100644 index 000000000..4fcf055df --- /dev/null +++ b/moto/elb/responses.py @@ -0,0 +1,179 @@ +from jinja2 import Template + +from moto.core.responses import BaseResponse +from .models import elb_backend + + +class ELBResponse(BaseResponse): + + def create_load_balancer(self): + """ + u'Scheme': [u'internet-facing'], + """ + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + availability_zones = [value[0] for key, value in self.querystring.items() if "AvailabilityZones.member" in key] + ports = [] + port_index = 1 + while True: + try: + protocol = self.querystring['Listeners.member.{}.Protocol'.format(port_index)][0] + except KeyError: + break + lb_port = self.querystring['Listeners.member.{}.LoadBalancerPort'.format(port_index)][0] + instance_port = self.querystring['Listeners.member.{}.InstancePort'.format(port_index)][0] + ports.append([protocol, lb_port, instance_port]) + port_index += 1 + elb_backend.create_load_balancer( + name=load_balancer_name, + zones=availability_zones, + ports=ports, + ) + template = Template(CREATE_LOAD_BALANCER_TEMPLATE) + return template.render() + + def describe_load_balancers(self): + names = [value[0] for key, value in self.querystring.items() if "LoadBalancerNames.member" in key] + load_balancers = elb_backend.describe_load_balancers(names) + template = Template(DESCRIBE_LOAD_BALANCERS_TEMPLATE) + return template.render(load_balancers=load_balancers) + + def delete_load_balancer(self): + load_balancer_name = self.querystring.get('LoadBalancerName')[0] + elb_backend.delete_load_balancer(load_balancer_name) + template = Template(DELETE_LOAD_BALANCER_TEMPLATE) + return template.render() + + def configure_health_check(self): + check = elb_backend.configure_health_check( + load_balancer_name=self.querystring.get('LoadBalancerName')[0], + timeout=self.querystring.get('HealthCheck.Timeout')[0], + healthy_threshold=self.querystring.get('HealthCheck.HealthyThreshold')[0], + unhealthy_threshold=self.querystring.get('HealthCheck.UnhealthyThreshold')[0], + interval=self.querystring.get('HealthCheck.Interval')[0], + target=self.querystring.get('HealthCheck.Target')[0], + ) + template = Template(CONFIGURE_HEALTH_CHECK_TEMPLATE) + return template.render(check=check) + + def register_instances_with_load_balancer(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] + template = Template(REGISTER_INSTANCES_TEMPLATE) + load_balancer = elb_backend.register_instances(load_balancer_name, instance_ids) + return template.render(load_balancer=load_balancer) + + def deregister_instances_from_load_balancer(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] + template = Template(DEREGISTER_INSTANCES_TEMPLATE) + load_balancer = elb_backend.deregister_instances(load_balancer_name, instance_ids) + return template.render(load_balancer=load_balancer) + +CREATE_LOAD_BALANCER_TEMPLATE = """ + tests.us-east-1.elb.amazonaws.com +""" + +DELETE_LOAD_BALANCER_TEMPLATE = """ +""" + +DESCRIBE_LOAD_BALANCERS_TEMPLATE = """ + + + {% for load_balancer in load_balancers %} + + + + {{ load_balancer.name }} + 2013-01-01T00:00:00.19000Z + + {% if load_balancer.health_check %} + {{ load_balancer.health_check.interval }} + {{ load_balancer.health_check.target }} + {{ load_balancer.health_check.healthy_threshold }} + {{ load_balancer.health_check.timeout }} + {{ load_balancer.health_check.unhealthy_threshold }} + {% endif %} + + vpc-56e10e3d + + {% for listener in load_balancer.listeners %} + + + AWSConsolePolicy-1 + + + {{ listener.protocol }} + {{ listener.load_balancer_port }} + {{ listener.protocol }} + {{ listener.instance_port }} + + + {% endfor %} + + + {% for instance_id in load_balancer.instance_ids %} + + {{ instance_id }} + + {% endfor %} + + + + + + + AWSConsolePolicy-1 + 30 + + + + + {% for zone in load_balancer.zones %} + {{ zone }} + {% endfor %} + + tests.us-east-1.elb.amazonaws.com + Z3ZONEID + internet-facing + tests.us-east-1.elb.amazonaws.com + + + + + {% endfor %} + + + + f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c + +""" + +CONFIGURE_HEALTH_CHECK_TEMPLATE = """ + + {{ check.interval }} + {{ check.target }} + {{ check.healthy_threshold }} + {{ check.timeout }} + {{ check.unhealthy_threshold }} + +""" + +REGISTER_INSTANCES_TEMPLATE = """ + + {% for instance_id in load_balancer.instance_ids %} + + {{ instance_id }} + + {% endfor %} + +""" + +DEREGISTER_INSTANCES_TEMPLATE = """ + + {% for instance_id in load_balancer.instance_ids %} + + {{ instance_id }} + + {% endfor %} + +""" diff --git a/moto/elb/urls.py b/moto/elb/urls.py new file mode 100644 index 000000000..e41ed2921 --- /dev/null +++ b/moto/elb/urls.py @@ -0,0 +1,9 @@ +from .responses import ELBResponse + +url_bases = [ + "https?://elasticloadbalancing.(.+).amazonaws.com", +] + +url_paths = { + '{0}/$': ELBResponse().dispatch, +} diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py new file mode 100644 index 000000000..11ddc0ced --- /dev/null +++ b/tests/test_elb/test_elb.py @@ -0,0 +1,121 @@ +import boto +from boto.ec2.elb import HealthCheck +import sure # flake8: noqa + +from moto import mock_elb, mock_ec2 + + +@mock_elb +def test_create_load_balancer(): + conn = boto.connect_elb() + + zones = ['us-east-1a', 'us-east-1b'] + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', zones, ports) + + balancers = conn.get_all_load_balancers() + balancer = balancers[0] + balancer.name.should.equal("my-lb") + set(balancer.availability_zones).should.equal(set(['us-east-1a', 'us-east-1b'])) + listener1 = balancer.listeners[0] + listener1.load_balancer_port.should.equal(80) + listener1.instance_port.should.equal(8080) + listener1.protocol.should.equal("HTTP") + listener2 = balancer.listeners[1] + listener2.load_balancer_port.should.equal(443) + listener2.instance_port.should.equal(8443) + listener2.protocol.should.equal("TCP") + + +@mock_elb +def test_get_load_balancers_by_name(): + conn = boto.connect_elb() + + zones = ['us-east-1a', 'us-east-1b'] + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb1', zones, ports) + lb = conn.create_load_balancer('my-lb2', zones, ports) + lb = conn.create_load_balancer('my-lb3', zones, ports) + + conn.get_all_load_balancers().should.have.length_of(3) + conn.get_all_load_balancers(load_balancer_names=['my-lb1']).should.have.length_of(1) + conn.get_all_load_balancers(load_balancer_names=['my-lb1', 'my-lb2']).should.have.length_of(2) + + +@mock_elb +def test_delete_load_balancer(): + conn = boto.connect_elb() + + zones = ['us-east-1a'] + ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] + lb = conn.create_load_balancer('my-lb', zones, ports) + + balancers = conn.get_all_load_balancers() + balancers.should.have.length_of(1) + + conn.delete_load_balancer("my-lb") + balancers = conn.get_all_load_balancers() + balancers.should.have.length_of(0) + + +@mock_elb +def test_create_health_check(): + conn = boto.connect_elb() + + hc = HealthCheck( + interval=20, + healthy_threshold=3, + unhealthy_threshold=5, + target='HTTP:8080/health', + timeout=23, + ) + + lb = conn.create_load_balancer('my-lb', [], []) + lb.configure_health_check(hc) + + balancer = conn.get_all_load_balancers()[0] + health_check = balancer.health_check + health_check.interval.should.equal(20) + health_check.healthy_threshold.should.equal(3) + health_check.unhealthy_threshold.should.equal(5) + health_check.target.should.equal('HTTP:8080/health') + health_check.timeout.should.equal(23) + + +@mock_ec2 +@mock_elb +def test_register_instances(): + ec2_conn = boto.connect_ec2() + reservation = ec2_conn.run_instances('ami-1234abcd', 2) + instance_id1 = reservation.instances[0].id + instance_id2 = reservation.instances[1].id + + conn = boto.connect_elb() + lb = conn.create_load_balancer('my-lb', [], []) + + lb.register_instances([instance_id1, instance_id2]) + + balancer = conn.get_all_load_balancers()[0] + instance_ids = [instance.id for instance in balancer.instances] + set(instance_ids).should.equal(set([instance_id1, instance_id2])) + + +@mock_ec2 +@mock_elb +def test_deregister_instances(): + ec2_conn = boto.connect_ec2() + reservation = ec2_conn.run_instances('ami-1234abcd', 2) + instance_id1 = reservation.instances[0].id + instance_id2 = reservation.instances[1].id + + conn = boto.connect_elb() + lb = conn.create_load_balancer('my-lb', [], []) + + lb.register_instances([instance_id1, instance_id2]) + + balancer = conn.get_all_load_balancers()[0] + balancer.instances.should.have.length_of(2) + balancer.deregister_instances([instance_id1]) + + balancer.instances.should.have.length_of(1) + balancer.instances[0].id.should.equal(instance_id2) diff --git a/tests/test_elb/test_server.py b/tests/test_elb/test_server.py new file mode 100644 index 000000000..e69de29bb