def format_stack_events(stack, limit=None): if hasattr(stack, 'describe_events'): events = stack.describe_events() else: cfn = boto.connect_cloudformation() events = cfn.describe_stack_events(stack.stack_name) cfn = boto.connect_cloudformation() events = list(cfn.describe_stack_events(stack.stack_name)) if limit is None: limit = len(events) tab = PrettyTable(['Time', 'Type', 'Logical ID', 'Status', 'Reason']) tab.align = 'l' for e in events: reason = e.resource_status_reason tab.add_row([ local_date(e.timestamp), e.resource_type, e.logical_resource_id, e.resource_status, reason if reason is not None else '' ]) return tab.get_string(end=limit)
def create_stack(stack_name, template, region='us-east-1', blocking=True, temp_bucket='edx-sandbox-devops'): cfn = boto.connect_cloudformation() # Upload the template to s3 key_name = 'cloudformation/auto/{}_{}'.format(stack_name, basename(template)) template_url = upload_file(template, temp_bucket, key_name) # Reference the stack. try: stack_id = cfn.create_stack(stack_name, template_url=template_url, capabilities=['CAPABILITY_IAM'], tags={'autostack':'true'}, parameters=[('KeyName', 'continuous-integration')]) except Exception as e: print(e.message) raise e status = None while blocking: sleep(5) stack_instance = cfn.describe_stacks(stack_id)[0] status = stack_instance.stack_status print(status) if 'COMPLETE' in status: break if status in FAILURE_STATES: raise Exception('Creation Failed. Stack Status: {}, ID:{}'.format( status, stack_id)) return stack_id
def main(): parser = OptionParser(usage="usage: %prog [options] stackname template") parser.add_option('-d','--debug',help="Debug level",default=0) parser.add_option('-g','--user',help="VPN User",default='user-00') (options,args) = parser.parse_args() if len(args) < 2: parser.print_help() sys.exit(1) (stackin, templatein) = args try: templin = open (templatein,'r').read() except IOError: sys.stderr.write("File "+templatein+" Does not exist!\n") parser.print_help() sys.exit(1) NEXTSTACK = options.user + "Stack" cfn = boto.connect_cloudformation() cfn_template = render_jinja(templatein) try: cfn.validate_template(cfn_template) except boto.exception.BotoServerError, e: print e.error_message
def test_do_poll_true_create_returns_false_on_non_existing_stack(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(False, cfno.do_poll(cfn, 'test', True, 'create'))
def format_stack_resources(stack): if hasattr(stack, 'describe_resources'): resources = stack.describe_resources() else: cfn = boto.connect_cloudformation() resources = cfn.describe_stack_resources(stack.stack_name) tab = PrettyTable(['Type', 'Status', 'Logical ID', 'Physical ID']) tab.align = 'l' tab.sortby = 'Type' for r in resources: # TODO: generalize this if r.resource_type == 'AWS::CloudFormation::WaitConditionHandle': physical_resource_id = r.physical_resource_id[0:45] + '...' else: physical_resource_id = r.physical_resource_id tab.add_row([ r.resource_type, r.resource_status, r.logical_resource_id, physical_resource_id ]) return tab.get_string()
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
def test__stack_exists_nonexistant(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(False, cfno._stack_exists(cfn, 'test'))
def main(): vpc = get_vpc() if vpc is None: raise LookupError("Unable to connect to VPC.") subnets = get_all_subnets(vpc) template = get_template(vpc, subnets) cfn = boto.connect_cloudformation() print("Creating CloudFormation stack in AWS.") cfn.create_stack( 'thunder', template_body=template, template_url=None, parameters=[], notification_arns=[], disable_rollback=False, timeout_in_minutes=None, capabilities=["CAPABILITY_IAM"], tags=dict( application="thunder", environment="test" ) ) print("Stack creation complete.")
def test_describe_stack_events_shows_create_update_and_delete(): conn = boto.connect_cloudformation() stack_id = conn.create_stack("test_stack", template_body=dummy_template_json) conn.update_stack(stack_id, template_body=dummy_template_json2) conn.delete_stack(stack_id) # assert begins and ends with stack events events = conn.describe_stack_events(stack_id) events[0].resource_type.should.equal("AWS::CloudFormation::Stack") events[-1].resource_type.should.equal("AWS::CloudFormation::Stack") # testing ordering of stack events without assuming resource events will not exist stack_events_to_look_for = iter([ ("CREATE_IN_PROGRESS", "User Initiated"), ("CREATE_COMPLETE", None), ("UPDATE_IN_PROGRESS", "User Initiated"), ("UPDATE_COMPLETE", None), ("DELETE_IN_PROGRESS", "User Initiated"), ("DELETE_COMPLETE", None)]) try: for event in events: event.stack_id.should.equal(stack_id) event.stack_name.should.equal("test_stack") if event.resource_type == "AWS::CloudFormation::Stack": event.logical_resource_id.should.equal("test_stack") event.physical_resource_id.should.equal(stack_id) status_to_look_for, reason_to_look_for = next(stack_events_to_look_for) event.resource_status.should.equal(status_to_look_for) if reason_to_look_for is not None: event.resource_status_reason.should.equal(reason_to_look_for) except StopIteration: assert False, "Too many stack events" list(stack_events_to_look_for).should.be.empty
def test_stack_sqs_integration(): sqs_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "QueueGroup": { "Type": "AWS::SQS::Queue", "Properties": { "QueueName": "my-queue", "VisibilityTimeout": 60, } }, }, } sqs_template_json = json.dumps(sqs_template) conn = boto.connect_cloudformation() conn.create_stack( "test_stack", template_body=sqs_template_json, ) stack = conn.describe_stacks()[0] queue = stack.describe_resources()[0] queue.resource_type.should.equal('AWS::SQS::Queue') queue.logical_resource_id.should.equal("QueueGroup") queue.physical_resource_id.should.equal("my-queue")
def test_stack_ec2_integration(): ec2_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "WebServerGroup": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "ami-1234abcd", "UserData": "some user data", } }, }, } ec2_template_json = json.dumps(ec2_template) conn = boto.connect_cloudformation() conn.create_stack( "ec2_stack", template_body=ec2_template_json, ) ec2_conn = boto.connect_ec2() reservation = ec2_conn.get_all_instances()[0] ec2_instance = reservation.instances[0] stack = conn.describe_stacks()[0] instance = stack.describe_resources()[0] instance.resource_type.should.equal('AWS::EC2::Instance') instance.logical_resource_id.should.equal("WebServerGroup") instance.physical_resource_id.should.equal(ec2_instance.id)
def test__get_difference_no_difference(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals([], cfno._get_difference(cfn, 'test', '{"a": "b"}'))
def test_delete_stack_by_name(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) conn.list_stacks().should.have.length_of(1) conn.delete_stack("test_stack") conn.list_stacks().should.have.length_of(0)
def test_create_stack(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) stack = conn.describe_stacks()[0] stack.stack_name.should.equal("test_stack") stack.get_template().should.equal(dummy_template)
def test__stack_updatable_limit_exceeded_does_not_exist(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(False, cfno._stack_updatable(cfn, 'test'))
def update_config(region_name, stack_params, config_file): parser = SafeConfigParser() parser.read(config_file) print 'Validating resource creation . . . ' try: cfn = boto.connect_cloudformation() cfn = boto.cloudformation.connect_to_region(region_name) stack = cfn.describe_stacks(stack_params['vpc_stack_name'])[0] vpc_id = 'undef' publicroute_id = 'undef' privateroute_id = 'undef' for output in stack.outputs: if output.key == 'VpcId': vpc_id = output.value elif output.key == 'PublicRouteTable': publicroute_id = output.value elif output.key == 'PrivateRouteTable': privateroute_id = output.value if vpc_id == 'undef': raise UndefError('VpcId') if publicroute_id == 'undef': raise UndefError('PublicRouteTable') if privateroute_id == 'undef': raise UndefError('PrivateRouteTable') print('Updating ' + config_file + ' with ' + stack_params['vpc_stack_name'] + ' stack information') parser.set('prod', 'vpc_id', vpc_id) parser.set('prod', 'public_subnet_route_table', publicroute_id) parser.set('prod', 'private_subnet_route_table', privateroute_id) config_output = open(config_file, 'w') parser.write(config_output) except UndefError as e: print 'Error updating configuration. Stack output not defined:', e.value except boto.exception.BotoServerError as e: print e.error_message
def test_update_stack_with_parameters(): dummy_template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Stack", "Resources": { "VPC": { "Properties": { "CidrBlock": { "Ref": "Bar" } }, "Type": "AWS::EC2::VPC", } }, "Parameters": { "Bar": { "Type": "String" } }, } dummy_template_json = json.dumps(dummy_template) conn = boto.connect_cloudformation() conn.create_stack( "test_stack", template_body=dummy_template_json, parameters=[("Bar", "192.168.0.0/16")], ) conn.update_stack( "test_stack", template_body=dummy_template_json, parameters=[("Bar", "192.168.0.1/16")], ) stack = conn.describe_stacks()[0] assert stack.parameters[0].value == "192.168.0.1/16"
def describe(self): if self.pargs.stack_name == None: print 'Please provide a stack name' sys.exit(1) try: conn = boto.connect_cloudformation() stacks = conn.describe_stacks(self.pargs.stack_name) stack = None if len(stacks) == 0: raise Exception("No stacks found matching '%s'" % self.pargs.stack_name) else: stack = stacks[0] stack_events = conn.describe_stack_events(self.pargs.stack_name) print '%s [%s]:' % (self.pargs.stack_name, stack.stack_status) print 'Creation Time\t: %s' % stack.creation_time print 'Description\t: %s' % stack.description if len(stack.outputs) > 0: print 'Outputs\t:' for output in stack.outputs: print '\t%s: %s' % (output.key, output.value) if len(stack_events) > 0: print 'Events\t:' for stack_event in stack_events: print '[%s]\t\t\t\t\t[%s] \n\t%s %s \n\t%s \n\tReason: %s \n_________________________________________________________________________________'% ( stack_event.timestamp, stack_event.resource_status, stack_event.resource_type, stack_event.logical_resource_id, stack_event.physical_resource_id, stack_event.resource_status_reason) except Exception as e: self.log.error(e) sys.exit(1)
def validate_url(bucket_base_name): """ Validate templates via URLs of S3 objects. """ regions = boto.cloudformation.regions() for region in regions: bucket_name = bucket_base_name + '-' + region.name try: cfn = boto.connect_cloudformation(region=region) for dirpath, dirnames, filenames in os.walk(sfn.TEMPLATES_DIR): # upload only *.template files filenames = [ filename for filename in filenames if filename.endswith('.template') ] print "Validating ", len(filenames), "templates in ", bucket_name, " ..." for filename in filenames: keyname = os.path.relpath(os.path.join(dirpath, filename)).replace('\\', '/') template_url = "http://s3" # handle S3 legacy issue regarding region 'US Standard', see e.g. # https://forums.aws.amazon.com/message.jspa?messageID=185820 if region.name != 'us-east-1': template_url += ('-' + region.name) template_url += ('.amazonaws.com/' + bucket_name + '/' + keyname) log.info("... validating {0} ...".format(keyname)) template = cfn.validate_template(template_url=template_url) except boto.exception.BotoServerError, e: log.error(e.error_message)
def setup_cfn_connection(self, endpoint=None, region=None, aws_access_key_id=None, aws_secret_access_key=None, path="/",port=443, is_secure=True, boto_debug=0): cfn_region = RegionInfo() if region: self.debug("Check region: " + str(region)) try: if not endpoint: cfn_region.endpoint = "cloudformation.{0}.amazonaws.com".format(region) else: cfn_region.endpoint = endpoint except KeyError: raise Exception( 'Unknown region: %s' % region) else: cfn_region.name = 'eucalyptus' if endpoint: cfn_region.endpoint = endpoint else: cfn_region.endpoint = self.get_cfn_ip() try: cfn_connection_args = { 'aws_access_key_id' : aws_access_key_id, 'aws_secret_access_key': aws_secret_access_key, 'is_secure': is_secure, 'debug':boto_debug, 'port' : port, 'path' : path, 'region' : cfn_region} self.debug("Attempting to create cloudformation connection to " + self.get_cfn_ip() + ':' + str(port) + path) self.cloudformation = boto.connect_cloudformation(**cfn_connection_args) except Exception, e: self.critical("Was unable to create cloudformation connection because of exception: " + str(e))
def test_update_stack_with_parameters(): dummy_template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Stack", "Resources": { "VPC": { "Properties": { "CidrBlock": {"Ref": "Bar"} }, "Type": "AWS::EC2::VPC" } }, "Parameters": { "Bar": { "Type": "String" } } } dummy_template_json = json.dumps(dummy_template) conn = boto.connect_cloudformation() conn.create_stack( "test_stack", template_body=dummy_template_json, parameters=[("Bar", "192.168.0.0/16")] ) conn.update_stack( "test_stack", template_body=dummy_template_json, parameters=[("Bar", "192.168.0.1/16")] ) stack = conn.describe_stacks()[0] assert stack.parameters[0].value == "192.168.0.1/16"
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
def test_do_poll_true_returns_true_multiple_loops(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(True, cfno.do_poll(cfn, 'test', True, 'create'))
def test_cloudformation_params(): dummy_template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Stack 1", "Resources": {}, "Parameters": { "APPNAME": { "Default": "app-name", "Description": "The name of the app", "Type": "String", } }, } dummy_template_json = json.dumps(dummy_template) cfn = boto.connect_cloudformation() cfn.create_stack( "test_stack1", template_body=dummy_template_json, parameters=[("APPNAME", "testing123")], ) stack = cfn.describe_stacks("test_stack1")[0] stack.parameters.should.have.length_of(1) param = stack.parameters[0] param.key.should.equal("APPNAME") param.value.should.equal("testing123")
def test_do_poll_true_delete_catches_rate_limit(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(True, cfno.do_poll(cfn, 'test', True, 'delete'))
def test_delete_stack_with_resource_missing_delete_attr(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json3) conn.describe_stacks().should.have.length_of(1) conn.delete_stack("test_stack") conn.describe_stacks().should.have.length_of(0)
def test__stack_updatable_exists(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(True, cfno._stack_updatable(cfn, 'test'))
def test_delete_stack_by_name(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) conn.describe_stacks().should.have.length_of(1) conn.delete_stack("test_stack") conn.describe_stacks().should.have.length_of(0)
def test_cloudformation_params_conditions_and_resources_are_distinct(): dummy_template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Stack 1", "Conditions": { "FooEnabled": {"Fn::Equals": [{"Ref": "FooEnabled"}, "true"]}, "FooDisabled": { "Fn::Not": [{"Fn::Equals": [{"Ref": "FooEnabled"}, "true"]}] }, }, "Parameters": { "FooEnabled": {"Type": "String", "AllowedValues": ["true", "false"]} }, "Resources": { "Bar": { "Properties": {"CidrBlock": "192.168.0.0/16"}, "Condition": "FooDisabled", "Type": "AWS::EC2::VPC", } }, } dummy_template_json = json.dumps(dummy_template) cfn = boto.connect_cloudformation() cfn.create_stack( "test_stack1", template_body=dummy_template_json, parameters=[("FooEnabled", "true")], ) stack = cfn.describe_stacks("test_stack1")[0] resources = stack.list_resources() assert not [ resource for resource in resources if resource.logical_resource_id == "Bar" ]
def test__show_prompt_no_difference(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) assert_equals(False, cfno._show_prompt(cfn, 'test', '{"a": "b"}', _mock_allowed_update(self)))
def deploy_stack(self, stack_name, template_string_or_url, capabilities=['CAPABILITY_IAM'], parameters=None, aws_region=None, wait_for_complete=True): """ Method takes a CloudFormation template string or S3 url and deploys the stack to the specified AWS region. @param stack_name [string] - name to use when deploying the CloudFormation stack. @param template_string_or_url [string] - S3 URL or CloudFormation template body to be deployed. @param capabiltiies [list(str)] - List of CloudFormation template capabilities to be granted to the deployed stack. @param parameters [dict] - dictionary of key value pairs containing overrides to template parameter defaults. @param aws_region [string] - AWS-specific region name to start when querying the AWS APIs @param wait_for_complete [boolean] - boolean indicating whether to poll for success or failure before completing the deploy process. """ if aws_region is None: aws_region = self.configuration.get('boto', {}).get( 'region_name', 'us-east-1') logging.debug( 'Setting default AWS Region for API access from overall configuration [' + aws_region + ']') logging.info('Connecting to CloudFormation in region [' + aws_region + ']') cf_conn = boto.connect_cloudformation(aws_region) logging.info('Starting deploy of stack [' + stack_name + '] to AWS in region [' + aws_region + ']') command_args = {'capabilities': capabilities} try: if type(template_string_or_url) == dict: command_args['template_body'] = json.dumps( template_string_or_url) else: template_dict = json.loads(template_string_or_url) command_args['template_body'] = template_string_or_url except: command_args['template_s3_url'] = template_string_or_url logging.debug('Calling stack deploy for [' + stack_name + '] with arguments: ' + json.dumps(command_args)) cf_conn.create_stack(stack_name, **command_args) if wait_for_complete: if self.wait_for_stack(cf_conn, stack_name): logging.info('Stack [' + stack_name + '] successfully deployed to AWS in region [' + aws_region + ']') return True else: message = 'Stack [%s] failed to deploy to AWS in region [%s] with status [%s]' % ( stack_name, aws_region, self.get_stack_status(cf_conn, stack_name)) logging.warn(message) return False else: return True
def test_list_stacks(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) conn.create_stack("test_stack2", template_body=dummy_template_json) stacks = conn.list_stacks() stacks.should.have.length_of(2) stacks[0].template_description.should.equal("Stack 1")
def orchestration_connection(self): if self._orchestration_connection is None: region = self._region(self._cfn_regions()) self._orchestration_connection = boto.connect_cloudformation( region=region, **config.CLOUD['ORCHESTRATION_CREDENTIALS'] ) return self._orchestration_connection
def test_describe_stack_by_stack_id(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) stack = conn.describe_stacks("test_stack")[0] stack_by_id = conn.describe_stacks(stack.stack_id)[0] stack_by_id.stack_id.should.equal(stack.stack_id) stack_by_id.stack_name.should.equal("test_stack")
def test__show_prompt_difference_yes(self): cfno = AWSCFNOutput() cfn = boto.connect_cloudformation( aws_access_key_id='access', aws_secret_access_key='secret' ) sys.stdout = open('/dev/null', 'w') assert_equals(True, cfno._show_prompt(cfn, 'test', '{"a": "c"}', _mock_allowed_update(self)))
def validate_cloudformation_template(template_body): """Validates the JSON of a CloudFormation template produced by Troposphere Arguments :param template_body: The string representation of CloudFormation template JSON """ c = boto.connect_cloudformation() return c.validate_template(template_body=template_body)
def test_get_template_by_name(): conn = boto.connect_cloudformation() conn.create_stack( "test_stack", template_body=dummy_template_json, ) template = conn.get_template("test_stack") template.should.equal(dummy_template)
def test_delete_stack_dynamo_template(): conn = boto.connect_cloudformation() db_conn = boto.dynamodb2.connect_to_region("us-east-1") # conn.create_stack("test_stack", template_body=dummy_template_json4) db_conn.list_tables()["TableNames"].should.have.length_of(1) # conn.delete_stack("test_stack") db_conn.list_tables()["TableNames"].should.have.length_of(0)
def test_list_stacks_with_filter(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) conn.create_stack("test_stack2", template_body=dummy_template_json) conn.update_stack("test_stack", template_body=dummy_template_json2) stacks = conn.list_stacks("CREATE_COMPLETE") stacks.should.have.length_of(1) stacks[0].template_description.should.equal("Stack 1") stacks = conn.list_stacks("UPDATE_COMPLETE") stacks.should.have.length_of(1)
def test_stack_tags(): conn = boto.connect_cloudformation() conn.create_stack( "test_stack", template_body=dummy_template_json, tags={"foo": "bar", "baz": "bleh"}, ) stack = conn.describe_stacks()[0] dict(stack.tags).should.equal({"foo": "bar", "baz": "bleh"})
def test_delete_stack_by_id(): conn = boto.connect_cloudformation() stack_id = conn.create_stack( "test_stack", template_body=dummy_template_json, ) conn.list_stacks().should.have.length_of(1) conn.delete_stack(stack_id) conn.list_stacks().should.have.length_of(0)
def test_create_stack(): conn = boto.connect_cloudformation() conn.create_stack( "test_stack", template_body=dummy_template_json, ) stack = conn.describe_stacks()[0] stack.stack_name.should.equal('test_stack') stack.get_template().should.equal(dummy_template)
def test_create_stack_with_notification_arn(): conn = boto.connect_cloudformation() conn.create_stack( "test_stack_with_notifications", template_body=dummy_template_json, notification_arns='arn:aws:sns:us-east-1:123456789012:fake-queue' ) stack = conn.describe_stacks()[0] [n.value for n in stack.notification_arns].should.contain('arn:aws:sns:us-east-1:123456789012:fake-queue')
def test_delete_stack_dynamo_template(): conn = boto.connect_cloudformation() dynamodb_client = boto3.client("dynamodb", region_name="us-east-1") conn.create_stack("test_stack", template_body=dummy_template4) table_desc = dynamodb_client.list_tables() len(table_desc.get("TableNames")).should.equal(1) conn.delete_stack("test_stack") table_desc = dynamodb_client.list_tables() len(table_desc.get("TableNames")).should.equal(0) conn.create_stack("test_stack", template_body=dummy_template4)
def status(args): """List the status of the instances and ELB.""" stacks = find_stacks(args.stack_name) cfn = boto.connect_cloudformation() for stack in stacks: fullstack = cfn.describe_stacks(stack.stack_name)[0] yield "\nStack %s" % fullstack.stack_name yield format_autoscale_instances(fullstack)
def test_stack_elb_integration_with_attached_ec2_instances(): elb_template = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "MyELB": { "Type": "AWS::ElasticLoadBalancing::LoadBalancer", "Instances": [{ "Ref": "Ec2Instance1" }], "Properties": { "LoadBalancerName": "test-elb", "AvailabilityZones": ['us-east1'], } }, "Ec2Instance1": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "ami-1234abcd", "UserData": "some user data", } }, }, } elb_template_json = json.dumps(elb_template) conn = boto.connect_cloudformation() conn.create_stack( "elb_stack", template_body=elb_template_json, ) elb_conn = boto.connect_elb() load_balancer = elb_conn.get_all_load_balancers()[0] ec2_conn = boto.connect_ec2() reservation = ec2_conn.get_all_instances()[0] ec2_instance = reservation.instances[0] instance_id = ec2_instance.id load_balancer.instances[0].id.should.equal(ec2_instance.id) list(load_balancer.availability_zones).should.equal(['us-east1']) load_balancer_name = load_balancer.name stack = conn.describe_stacks()[0] stack_resources = stack.describe_resources() stack_resources.should.have.length_of(2) for resource in stack_resources: if resource.resource_type == 'AWS::ElasticLoadBalancing::LoadBalancer': load_balancer = resource else: ec2_instance = resource load_balancer.logical_resource_id.should.equal("MyELB") load_balancer.physical_resource_id.should.equal(load_balancer_name) ec2_instance.physical_resource_id.should.equal(instance_id)
def test_delete_stack_by_id(): conn = boto.connect_cloudformation() stack_id = conn.create_stack("test_stack", template_body=dummy_template_json) conn.describe_stacks().should.have.length_of(1) conn.delete_stack(stack_id) conn.describe_stacks().should.have.length_of(0) with assert_raises(BotoServerError): conn.describe_stacks("test_stack") conn.describe_stacks(stack_id).should.have.length_of(1)
def test_describe_deleted_stack(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json) stack = conn.describe_stacks("test_stack")[0] stack_id = stack.stack_id conn.delete_stack(stack.stack_id) stack_by_id = conn.describe_stacks(stack_id)[0] stack_by_id.stack_id.should.equal(stack.stack_id) stack_by_id.stack_name.should.equal("test_stack") stack_by_id.stack_status.should.equal("DELETE_COMPLETE")
def test_create_stack_with_notification_arn(): conn = boto.connect_cloudformation() conn.create_stack( "test_stack_with_notifications", template_body=dummy_template_json, notification_arns="arn:aws:sns:us-east-1:{}:fake-queue".format( ACCOUNT_ID), ) stack = conn.describe_stacks()[0] [n.value for n in stack.notification_arns ].should.contain("arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID))
def test_describe_stack_events_shows_create_update_and_delete(): conn = boto.connect_cloudformation() stack_id = conn.create_stack("test_stack", template_body=dummy_template_json) conn.update_stack(stack_id, template_body=dummy_template_json2) conn.delete_stack(stack_id) # assert begins and ends with stack events events = conn.describe_stack_events(stack_id) events[0].resource_type.should.equal("AWS::CloudFormation::Stack") events[-1].resource_type.should.equal("AWS::CloudFormation::Stack") # testing ordering of stack events without assuming resource events will not exist # the AWS API returns events in reverse chronological order stack_events_to_look_for = iter([ ("DELETE_COMPLETE", None), ("DELETE_IN_PROGRESS", "User Initiated"), ("UPDATE_COMPLETE", None), ("UPDATE_IN_PROGRESS", "User Initiated"), ("CREATE_COMPLETE", None), ("CREATE_IN_PROGRESS", "User Initiated"), ]) try: for event in events: event.stack_id.should.equal(stack_id) event.stack_name.should.equal("test_stack") event.event_id.should.match( r"[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}") if event.resource_type == "AWS::CloudFormation::Stack": event.logical_resource_id.should.equal("test_stack") event.physical_resource_id.should.equal(stack_id) status_to_look_for, reason_to_look_for = next( stack_events_to_look_for) event.resource_status.should.equal(status_to_look_for) if reason_to_look_for is not None: event.resource_status_reason.should.equal( reason_to_look_for) except StopIteration: assert False, "Too many stack events" list(stack_events_to_look_for).should.be.empty with pytest.raises(BotoServerError) as exp: conn.describe_stack_events("non_existing_stack") err = exp.value err.message.should.equal("Stack with id non_existing_stack does not exist") err.body.should.match(r"Stack with id non_existing_stack does not exist") err.error_code.should.equal("ValidationError") err.reason.should.equal("Bad Request") err.status.should.equal(400)
def test_update_stack_replace_tags(): conn = boto.connect_cloudformation() conn.create_stack("test_stack", template_body=dummy_template_json, tags={"foo": "bar"}) conn.update_stack("test_stack", template_body=dummy_template_json, tags={"foo": "baz"}) stack = conn.describe_stacks()[0] stack.stack_status.should.equal("UPDATE_COMPLETE") # since there is one tag it doesn't come out as a list dict(stack.tags).should.equal({"foo": "baz"})