From 1539c201a2dc0720c423dfd84c8381e02a576680 Mon Sep 17 00:00:00 2001 From: Niall Creech Date: Mon, 20 Jul 2015 13:09:51 +0100 Subject: [PATCH] Use a automatic resource naming Currently we use hard coded names for resources like S3. This change will allow us to use dynamic names auto-generated by aws and exposed as an output. (Closes ministryofjustice/bootstrap-cfn#123) - S3 bucket names are optional in config, default to dynamic ones - Add a method to fetch all the buckets for this stack - PEP8 fixes --- bootstrap_cfn/cloudformation.py | 40 ++++++++++++++++++++++++++++----- bootstrap_cfn/config.py | 34 +++++++++++++++++++--------- bootstrap_cfn/ec2.py | 1 + bootstrap_cfn/elb.py | 3 +-- bootstrap_cfn/errors.py | 2 +- bootstrap_cfn/utils.py | 1 - 6 files changed, 61 insertions(+), 20 deletions(-) diff --git a/bootstrap_cfn/cloudformation.py b/bootstrap_cfn/cloudformation.py index ede6cf4..5ba961e 100644 --- a/bootstrap_cfn/cloudformation.py +++ b/bootstrap_cfn/cloudformation.py @@ -58,11 +58,41 @@ def get_stack_load_balancers(self, stack_name_or_id): load_balancers: Set of stack resources containing only load balancers for this stack """ + resource_type = 'AWS::ElasticLoadBalancing::LoadBalancer' + return self.get_resource_type(stack_name_or_id, resource_type) + + def get_s3_buckets(self, stack_name_or_id): + """ + Collect up the S3 bucket stack resources + + Args: + stack_name_or_id (string): Name or id used to identify the stack + + Returns: + buckets: Set of stack resources containing only + buckets for this stack + """ + # get the stack + resource_type = "AWS::S3::Bucket" + return self.get_resource_type(stack_name_or_id, resource_type) + + def get_resource_type(self, stack_name_or_id, resource_type): + """ + Collect up a set of specific stack resources + + Args: + stack_name_or_id (string): Name or id used to identify the stack + resource_type(string): The resource type identifier + + Returns: + resources: Set of stack resources containing only + load balancers for this stack + """ # get the stack - load_balancers = [] + resources = [] stack = self.conn_cfn.describe_stacks(stack_name_or_id) if stack: - fn = lambda x: x.resource_type == 'AWS::ElasticLoadBalancing::LoadBalancer' - # get the load balancers group - load_balancers = filter(fn, stack[0].list_resources()) - return load_balancers + fn = lambda x: x.resource_type == resource_type + # get the resources + resources = filter(fn, stack[0].list_resources()) + return resources diff --git a/bootstrap_cfn/config.py b/bootstrap_cfn/config.py index 31d045c..e0b6bb4 100644 --- a/bootstrap_cfn/config.py +++ b/bootstrap_cfn/config.py @@ -90,6 +90,11 @@ def process(self): if 's3' in self.data: s3 = self.s3() map(template.add_resource, s3) + template.add_output(Output( + "BucketName", + Description="S3 bucket name", + Value=Ref("StaticBucket") + )) template = json.loads(template.to_json()) if 'includes' in self.data: @@ -264,7 +269,6 @@ def iam(self): def s3(self): # REQUIRED FIELDS AND MAPPING required_fields = { - 'static-bucket-name': 'BucketName' } # TEST FOR REQUIRED FIELDS AND EXIT IF MISSING ANY @@ -274,19 +278,27 @@ def s3(self): print "\n\n[ERROR] Missing S3 fields [%s]" % i sys.exit(1) - bucket = Bucket( - "StaticBucket", - AccessControl="BucketOwnerFullControl", - BucketName=self.data['s3']['static-bucket-name'], - ) + # If we have a bucket name set in config, then use it + # otherwise default to random + if 'static-bucket-name' in self.data['s3']: + bucket = Bucket( + "StaticBucket", + AccessControl="BucketOwnerFullControl", + BucketName=self.data['s3']['static-bucket-name'], + ) + else: + bucket = Bucket( + "StaticBucket", + AccessControl="BucketOwnerFullControl", + ) if 'policy' in present_keys: policy = json.loads(open(self.data['s3']['policy']).read()) else: - arn = 'arn:aws:s3:::%s/*' % self.data['s3']['static-bucket-name'] + arn = Join("", ["arn:aws:s3:::", Ref(bucket), "/*"]) policy = { 'Action': ['s3:GetObject'], - 'Resource': arn, + "Resource": [arn], 'Effect': 'Allow', 'Principal': '*'} @@ -465,7 +477,7 @@ def elb(self): elb_role_policies = PolicyType( "Policy" + safe_name, - PolicyName=safe_name+"BaseHost", + PolicyName=safe_name + "BaseHost", PolicyDocument={"Statement": [{ "Action": [ "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", @@ -478,7 +490,7 @@ def elb(self): ":", Ref("AWS::AccountId"), ':loadbalancer/%s' % load_balancer.LoadBalancerName - ]) + ]) ], "Effect": "Allow"} ]}, @@ -507,7 +519,7 @@ def elb(self): "FromPort": 443, "ToPort": 443, "CidrIp": "0.0.0.0/0" - }, + }, { "IpProtocol": "tcp", "FromPort": 80, diff --git a/bootstrap_cfn/ec2.py b/bootstrap_cfn/ec2.py index 4c0cb3e..566589e 100644 --- a/bootstrap_cfn/ec2.py +++ b/bootstrap_cfn/ec2.py @@ -3,6 +3,7 @@ from bootstrap_cfn import cloudformation from bootstrap_cfn import utils + class EC2: conn_cfn = None diff --git a/bootstrap_cfn/elb.py b/bootstrap_cfn/elb.py index 611cd78..28fd861 100644 --- a/bootstrap_cfn/elb.py +++ b/bootstrap_cfn/elb.py @@ -67,7 +67,7 @@ def set_ssl_certificates(self, ssl_config, stack_name): if protocol == "HTTPS": logging.info("ELB::set_ssl_certificates: " "Found HTTPS protocol on '%s', " - "updating SSL certificate with '%s'" + "updating SSL certificate with '%s'" % (load_balancer.name, cert_arn)) self.conn_elb.set_lb_listener_SSL_certificate(load_balancer.name, out_port, @@ -83,4 +83,3 @@ def set_ssl_certificates(self, ssl_config, stack_name): "No load balancers found in stack,") return updated_load_balancers - diff --git a/bootstrap_cfn/errors.py b/bootstrap_cfn/errors.py index 474bcd2..0f8cc37 100644 --- a/bootstrap_cfn/errors.py +++ b/bootstrap_cfn/errors.py @@ -4,7 +4,7 @@ class BootstrapCfnError(Exception): def __init__(self, msg): super(BootstrapCfnError, self).__init__(msg) - print >> sys.stderr, "[ERROR] {0}: {1}".format(self.__class__.__name__, msg) + print >> sys.stderr, "[ERROR] {0}: {1}".format(self.__class__.__name__, msg) class CfnConfigError(BootstrapCfnError): diff --git a/bootstrap_cfn/utils.py b/bootstrap_cfn/utils.py index 1c458fb..fb0506e 100644 --- a/bootstrap_cfn/utils.py +++ b/bootstrap_cfn/utils.py @@ -78,7 +78,6 @@ def tail(stack, stack_name): from fabric.colors import green, red, yellow """Show and then tail the event log""" - def colorize(e): if e.endswith("_IN_PROGRESS"): return yellow(e)