From 73ede75c392e43de47ba804ea165256c5ab39bd3 Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Wed, 19 Jul 2017 12:20:01 -0700 Subject: [PATCH 1/7] Adding test for ELBv1 security groups --- tests/test_elb/test_elb.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py index f9019eed2..681ffb830 100644 --- a/tests/test_elb/test_elb.py +++ b/tests/test_elb/test_elb.py @@ -143,6 +143,28 @@ def test_describe_paginated_balancers(): assert 'NextToken' not in resp2.keys() +@mock_elb +@mock_ec2 +def test_add_and_remove_security_groups(): + client = boto3.client('elb', region_name='us-east-1') + ec2 = boto3.resource('ec2', region_name='us-west-1') + + vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') + security_group = ec2.create_security_group( + GroupName='sg01', Description='Test security group sg01', VpcId=vpc.id) + + client.create_load_balancer( + LoadBalancerName='my-lb', + Listeners=[ + {'Protocol': 'tcp', 'LoadBalancerPort': 80, 'InstancePort': 8080}], + AvailabilityZones=['us-east-1a', 'us-east-1b'] + ) + + response = client.apply_security_groups_to_load_balancer( + LoadBalancerName='my-lb', + SecurityGroups=[security_group.id]) + assert response['SecurityGroups'] == [security_group.id] + @mock_elb_deprecated def test_add_listener(): From 7d0a575ab10ee8b7975076238d2aafc2472c2a3a Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Wed, 19 Jul 2017 12:20:58 -0700 Subject: [PATCH 2/7] Removing unused import --- tests/test_elb/test_elb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py index 681ffb830..1feed433e 100644 --- a/tests/test_elb/test_elb.py +++ b/tests/test_elb/test_elb.py @@ -11,7 +11,6 @@ from boto.ec2.elb.attributes import ( ) from boto.ec2.elb.policies import ( Policies, - AppCookieStickinessPolicy, LBCookieStickinessPolicy, OtherPolicy, ) From b512316c828f8c484f7fd781dde031975ca61205 Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Wed, 19 Jul 2017 12:36:04 -0700 Subject: [PATCH 3/7] removing further unused imports --- tests/test_elb/test_elb.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py index 1feed433e..35dddc39e 100644 --- a/tests/test_elb/test_elb.py +++ b/tests/test_elb/test_elb.py @@ -9,11 +9,6 @@ from boto.ec2.elb.attributes import ( ConnectionDrainingAttribute, AccessLogAttribute, ) -from boto.ec2.elb.policies import ( - Policies, - LBCookieStickinessPolicy, - OtherPolicy, -) from botocore.exceptions import ClientError from boto.exception import BotoServerError from nose.tools import assert_raises From 6ed8d12317f2f223767320930d8f886472796d05 Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Wed, 19 Jul 2017 15:58:49 -0700 Subject: [PATCH 4/7] Enforcing ELB security groups must be real --- moto/ec2/models.py | 2 +- moto/elb/exceptions.py | 8 ++++++++ moto/elb/models.py | 27 ++++++++++++++++++++++++--- moto/elb/responses.py | 34 ++++++++++++++++++++++++++++------ tests/test_elb/test_elb.py | 21 ++++++++++++++++++--- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 7e3df9880..6c35093e2 100755 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -3553,8 +3553,8 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, DHCPOptionsSetBackend, NetworkAclBackend, VpnGatewayBackend, CustomerGatewayBackend, NatGatewayBackend): def __init__(self, region_name): - super(EC2Backend, self).__init__() self.region_name = region_name + super(EC2Backend, self).__init__() # Default VPC exists by default, which is the current behavior # of EC2-VPC. See for detail: diff --git a/moto/elb/exceptions.py b/moto/elb/exceptions.py index 6c316ef47..3ea6a1642 100644 --- a/moto/elb/exceptions.py +++ b/moto/elb/exceptions.py @@ -64,3 +64,11 @@ class EmptyListenersError(ELBClientError): super(EmptyListenersError, self).__init__( "ValidationError", "Listeners cannot be empty") + + +class InvalidSecurityGroupError(ELBClientError): + + def __init__(self): + super(InvalidSecurityGroupError, self).__init__( + "ValidationError", + "One or more of the specified security groups do not exist.") diff --git a/moto/elb/models.py b/moto/elb/models.py index d09548340..504c68908 100644 --- a/moto/elb/models.py +++ b/moto/elb/models.py @@ -20,6 +20,7 @@ from .exceptions import ( DuplicateLoadBalancerName, DuplicateListenerError, EmptyListenersError, + InvalidSecurityGroupError, LoadBalancerNotFoundError, TooManyTagsError, ) @@ -63,7 +64,7 @@ class FakeBackend(BaseModel): class FakeLoadBalancer(BaseModel): - def __init__(self, name, zones, ports, scheme='internet-facing', vpc_id=None, subnets=None): + def __init__(self, name, zones, ports, scheme='internet-facing', vpc_id=None, subnets=None, security_groups=None): self.name = name self.health_check = None self.instance_ids = [] @@ -77,6 +78,7 @@ class FakeLoadBalancer(BaseModel): self.policies.other_policies = [] self.policies.app_cookie_stickiness_policies = [] self.policies.lb_cookie_stickiness_policies = [] + self.security_groups = security_groups or [] self.subnets = subnets or [] self.vpc_id = vpc_id or 'vpc-56e10e3d' self.tags = {} @@ -233,7 +235,7 @@ class ELBBackend(BaseBackend): self.__dict__ = {} self.__init__(region_name) - def create_load_balancer(self, name, zones, ports, scheme='internet-facing', subnets=None): + def create_load_balancer(self, name, zones, ports, scheme='internet-facing', subnets=None, security_groups=None): vpc_id = None ec2_backend = ec2_backends[self.region_name] if subnets: @@ -243,8 +245,19 @@ class ELBBackend(BaseBackend): raise DuplicateLoadBalancerName(name) if not ports: raise EmptyListenersError() + if not security_groups: + security_groups = [] + for security_group in security_groups: + if ec2_backend.get_security_group_from_id(security_group) is None: + raise InvalidSecurityGroupError() new_load_balancer = FakeLoadBalancer( - name=name, zones=zones, ports=ports, scheme=scheme, subnets=subnets, vpc_id=vpc_id) + name=name, + zones=zones, + ports=ports, + scheme=scheme, + subnets=subnets, + security_groups=security_groups, + vpc_id=vpc_id) self.load_balancers[name] = new_load_balancer return new_load_balancer @@ -302,6 +315,14 @@ class ELBBackend(BaseBackend): def get_load_balancer(self, load_balancer_name): return self.load_balancers.get(load_balancer_name) + def apply_security_groups_to_load_balancer(self, load_balancer_name, security_group_ids): + load_balancer = self.load_balancers.get(load_balancer_name) + ec2_backend = ec2_backends[self.region_name] + for security_group_id in security_group_ids: + if ec2_backend.get_security_group_from_id(security_group_id) is None: + raise InvalidSecurityGroupError() + load_balancer.security_groups = security_group_ids + def configure_health_check(self, load_balancer_name, timeout, healthy_threshold, unhealthy_threshold, interval, target): diff --git a/moto/elb/responses.py b/moto/elb/responses.py index ec20486f0..659c454b1 100644 --- a/moto/elb/responses.py +++ b/moto/elb/responses.py @@ -27,6 +27,7 @@ class ELBResponse(BaseResponse): ports = self._get_list_prefix("Listeners.member") scheme = self._get_param('Scheme') subnets = self._get_multi_param("Subnets.member") + security_groups = self._get_multi_param("SecurityGroups.member") load_balancer = self.elb_backend.create_load_balancer( name=load_balancer_name, @@ -34,6 +35,7 @@ class ELBResponse(BaseResponse): ports=ports, scheme=scheme, subnets=subnets, + security_groups=security_groups, ) self._add_tags(load_balancer) template = self.response_template(CREATE_LOAD_BALANCER_TEMPLATE) @@ -84,6 +86,13 @@ class ELBResponse(BaseResponse): template = self.response_template(DELETE_LOAD_BALANCER_TEMPLATE) return template.render() + def apply_security_groups_to_load_balancer(self): + load_balancer_name = self._get_param('LoadBalancerName') + security_group_ids = self._get_multi_param("SecurityGroups.member") + self.elb_backend.apply_security_groups_to_load_balancer(load_balancer_name, security_group_ids) + template = self.response_template(APPLY_SECURITY_GROUPS_TEMPLATE) + return template.render(security_group_ids=security_group_ids) + def configure_health_check(self): check = self.elb_backend.configure_health_check( load_balancer_name=self._get_param('LoadBalancerName'), @@ -99,8 +108,7 @@ class ELBResponse(BaseResponse): def register_instances_with_load_balancer(self): load_balancer_name = self._get_param('LoadBalancerName') - instance_ids = [value[0] for key, value in self.querystring.items( - ) if "Instances.member" in key] + instance_ids = [param.values()[0] for param in self._get_list_prefix('Instances.member')] template = self.response_template(REGISTER_INSTANCES_TEMPLATE) load_balancer = self.elb_backend.register_instances( load_balancer_name, instance_ids) @@ -119,8 +127,7 @@ class ELBResponse(BaseResponse): def deregister_instances_from_load_balancer(self): load_balancer_name = self._get_param('LoadBalancerName') - instance_ids = [value[0] for key, value in self.querystring.items( - ) if "Instances.member" in key] + instance_ids = [param.values()[0] for param in self._get_list_prefix('Instances.member')] template = self.response_template(DEREGISTER_INSTANCES_TEMPLATE) load_balancer = self.elb_backend.deregister_instances( load_balancer_name, instance_ids) @@ -252,8 +259,7 @@ class ELBResponse(BaseResponse): def describe_instance_health(self): load_balancer_name = self._get_param('LoadBalancerName') - instance_ids = [value[0] for key, value in self.querystring.items( - ) if "Instances.member" in key] + instance_ids = [param.values()[0] for param in self._get_list_prefix('Instances.member')] if len(instance_ids) == 0: instance_ids = self.elb_backend.get_load_balancer( load_balancer_name).instance_ids @@ -400,6 +406,9 @@ DESCRIBE_LOAD_BALANCERS_TEMPLATE = """ + + + {% for security_group_id in security_group_ids %} + {{ security_group_id }} + {% endfor %} + + + + f9880f01-7852-629d-a6c3-3ae2-666a409287e6dc0c + +""" + CONFIGURE_HEALTH_CHECK_TEMPLATE = """ diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py index 35dddc39e..98ec7d8e6 100644 --- a/tests/test_elb/test_elb.py +++ b/tests/test_elb/test_elb.py @@ -18,17 +18,22 @@ from moto import mock_elb, mock_ec2, mock_elb_deprecated, mock_ec2_deprecated @mock_elb_deprecated +@mock_ec2_deprecated def test_create_load_balancer(): conn = boto.connect_elb() + ec2 = boto.connect_ec2('the_key', 'the_secret') + + security_group = ec2.create_security_group('sg-abc987', 'description') zones = ['us-east-1a', 'us-east-1b'] ports = [(80, 8080, 'http'), (443, 8443, 'tcp')] - conn.create_load_balancer('my-lb', zones, ports, scheme='internal') + conn.create_load_balancer('my-lb', zones, ports, scheme='internal', security_groups=[security_group.id]) balancers = conn.get_all_load_balancers() balancer = balancers[0] balancer.name.should.equal("my-lb") balancer.scheme.should.equal("internal") + list(balancer.security_groups).should.equal([security_group.id]) set(balancer.availability_zones).should.equal( set(['us-east-1a', 'us-east-1b'])) listener1 = balancer.listeners[0] @@ -139,9 +144,9 @@ def test_describe_paginated_balancers(): @mock_elb @mock_ec2 -def test_add_and_remove_security_groups(): +def test_apply_security_groups_to_load_balancer(): client = boto3.client('elb', region_name='us-east-1') - ec2 = boto3.resource('ec2', region_name='us-west-1') + ec2 = boto3.resource('ec2', region_name='us-east-1') vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16') security_group = ec2.create_security_group( @@ -157,7 +162,17 @@ def test_add_and_remove_security_groups(): response = client.apply_security_groups_to_load_balancer( LoadBalancerName='my-lb', SecurityGroups=[security_group.id]) + assert response['SecurityGroups'] == [security_group.id] + balancer = client.describe_load_balancers()['LoadBalancerDescriptions'][0] + assert balancer['SecurityGroups'] == [security_group.id] + + # Usign a not-real security group raises an error + with assert_raises(ClientError) as error: + response = client.apply_security_groups_to_load_balancer( + LoadBalancerName='my-lb', + SecurityGroups=['not-really-a-security-group']) + assert "One or more of the specified security groups do not exist." in str(error.exception) @mock_elb_deprecated From 45d723044099d19ced81118c2ebc7c87b79d65ed Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Wed, 19 Jul 2017 16:01:00 -0700 Subject: [PATCH 5/7] fixing typo --- tests/test_elb/test_elb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py index 98ec7d8e6..5827e70c7 100644 --- a/tests/test_elb/test_elb.py +++ b/tests/test_elb/test_elb.py @@ -167,7 +167,7 @@ def test_apply_security_groups_to_load_balancer(): balancer = client.describe_load_balancers()['LoadBalancerDescriptions'][0] assert balancer['SecurityGroups'] == [security_group.id] - # Usign a not-real security group raises an error + # Using a not-real security group raises an error with assert_raises(ClientError) as error: response = client.apply_security_groups_to_load_balancer( LoadBalancerName='my-lb', From 7d00f6e92c1434d55be4d4da6c2b8bc2ad091433 Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Wed, 19 Jul 2017 16:33:24 -0700 Subject: [PATCH 6/7] python 2 support for dict_values --- moto/elb/responses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moto/elb/responses.py b/moto/elb/responses.py index 659c454b1..b1980c9b2 100644 --- a/moto/elb/responses.py +++ b/moto/elb/responses.py @@ -108,7 +108,7 @@ class ELBResponse(BaseResponse): def register_instances_with_load_balancer(self): load_balancer_name = self._get_param('LoadBalancerName') - instance_ids = [param.values()[0] for param in self._get_list_prefix('Instances.member')] + instance_ids = [list(param.values())[0] for param in self._get_list_prefix('Instances.member')] template = self.response_template(REGISTER_INSTANCES_TEMPLATE) load_balancer = self.elb_backend.register_instances( load_balancer_name, instance_ids) @@ -127,7 +127,7 @@ class ELBResponse(BaseResponse): def deregister_instances_from_load_balancer(self): load_balancer_name = self._get_param('LoadBalancerName') - instance_ids = [param.values()[0] for param in self._get_list_prefix('Instances.member')] + instance_ids = [list(param.values())[0] for param in self._get_list_prefix('Instances.member')] template = self.response_template(DEREGISTER_INSTANCES_TEMPLATE) load_balancer = self.elb_backend.deregister_instances( load_balancer_name, instance_ids) @@ -259,7 +259,7 @@ class ELBResponse(BaseResponse): def describe_instance_health(self): load_balancer_name = self._get_param('LoadBalancerName') - instance_ids = [param.values()[0] for param in self._get_list_prefix('Instances.member')] + instance_ids = [list(param.values())[0] for param in self._get_list_prefix('Instances.member')] if len(instance_ids) == 0: instance_ids = self.elb_backend.get_load_balancer( load_balancer_name).instance_ids From 115b9cee3e823208fa8ff389f6fd955b849ae673 Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Thu, 20 Jul 2017 14:25:46 +1000 Subject: [PATCH 7/7] add CloudFormation model for Kinesis streams --- moto/cloudformation/parsing.py | 2 ++ moto/kinesis/models.py | 7 +++++ .../test_cloudformation_stack_crud.py | 29 ++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 928cd68e0..923ada058 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -15,6 +15,7 @@ from moto.ec2 import models as ec2_models from moto.ecs import models as ecs_models from moto.elb import models as elb_models from moto.iam import models as iam_models +from moto.kinesis import models as kinesis_models from moto.kms import models as kms_models from moto.rds import models as rds_models from moto.rds2 import models as rds2_models @@ -31,6 +32,7 @@ MODEL_MAP = { "AWS::AutoScaling::AutoScalingGroup": autoscaling_models.FakeAutoScalingGroup, "AWS::AutoScaling::LaunchConfiguration": autoscaling_models.FakeLaunchConfiguration, "AWS::DynamoDB::Table": dynamodb_models.Table, + "AWS::Kinesis::Stream": kinesis_models.Stream, "AWS::Lambda::EventSourceMapping": lambda_models.EventSourceMapping, "AWS::Lambda::Function": lambda_models.LambdaFunction, "AWS::Lambda::Version": lambda_models.LambdaVersion, diff --git a/moto/kinesis/models.py b/moto/kinesis/models.py index 13900e6a6..aae94bbbd 100644 --- a/moto/kinesis/models.py +++ b/moto/kinesis/models.py @@ -172,6 +172,13 @@ class Stream(BaseModel): } } + @classmethod + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): + properties = cloudformation_json['Properties'] + region = properties.get('Region', 'us-east-1') + shard_count = properties.get('ShardCount', 1) + return Stream(properties['Name'], shard_count, region) + class FirehoseRecord(BaseModel): diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index 0e3634756..801faf8a1 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -569,7 +569,6 @@ def test_describe_stack_events_shows_create_update_and_delete(): @mock_cloudformation_deprecated -@mock_route53_deprecated def test_create_stack_lambda_and_dynamodb(): conn = boto.connect_cloudformation() dummy_template = { @@ -643,3 +642,31 @@ def test_create_stack_lambda_and_dynamodb(): stack = conn.describe_stacks()[0] resources = stack.list_resources() assert len(resources) == 4 + + +@mock_cloudformation_deprecated +def test_create_stack_kinesis(): + conn = boto.connect_cloudformation() + dummy_template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Stack Kinesis Test 1", + "Parameters": {}, + "Resources": { + "stream1": { + "Type" : "AWS::Kinesis::Stream", + "Properties" : { + "Name": "stream1", + "ShardCount": 2 + } + } + } + } + conn.create_stack( + "test_stack_kinesis_1", + template_body=json.dumps(dummy_template), + parameters={}.items() + ) + + stack = conn.describe_stacks()[0] + resources = stack.list_resources() + assert len(resources) == 1