def test_flatten(): """Ensure flatten method can really flatten an array""" input_arr = [1, 2, [3, 4], [5, 6, 7]] output_arr = utils.flatten(input_arr) assert output_arr == range(1, 8)
def create_or_update_stack(aws_account, region, ebs_bucket_name): """Handle creating or updating the ebs-snapper stack, and waiting""" # check for stack, create it if necessary stack_name = 'ebs-snapper-{}'.format(aws_account) cf_client = boto3.client('cloudformation', region_name=region) template_url = "https://s3.amazonaws.com/{}/cloudformation.json".format( ebs_bucket_name) try: LOG.info('Creating stack from %s', template_url) # only required parameter DEFAULT_STACK_PARAMS.append({ 'ParameterKey': 'LambdaS3Bucket', 'ParameterValue': ebs_bucket_name, 'UsePreviousValue': False }) response = cf_client.create_stack(StackName=stack_name, TemplateURL=template_url, Parameters=DEFAULT_STACK_PARAMS, Capabilities=[ 'CAPABILITY_IAM', ]) LOG.debug(response) LOG.warn("Wait while the stack %s is created.", stack_name) except ClientError as e: if not e.response['Error']['Code'] == 'AlreadyExistsException': raise try: LOG.info('Stack exists, updating stack from %s', template_url) # we can't specify "UsePreviousValue" if template didn't have this # param before our update. We can only UsePreviousValue if param # is already present in previous version of this template. sn = stack_name sr = cf_client.describe_stacks(StackName=sn) es_stack = [ x for x in sr.get('Stacks', []) if x['StackName'] == sn ] es_params = [x.get('Parameters', []) for x in es_stack] es_param_keys = [ x['ParameterKey'] for x in utils.flatten(es_params) ] # else we will get the default template value for this param params = [] for k in es_param_keys: params.append({'ParameterKey': k, 'UsePreviousValue': True}) response = cf_client.update_stack(StackName=stack_name, TemplateURL=template_url, Parameters=params, Capabilities=[ 'CAPABILITY_IAM', ]) LOG.debug(response) LOG.warn("Waiting while the stack %s is being updated.", stack_name) except ClientError as f: validation_error = f.response['Error']['Code'] == 'ValidationError' no_updates = f.response['Error'][ 'Message'] == 'No updates are to be performed.' if not validation_error and not no_updates: raise LOG.warn('No changes. Stack was not updated.') # wait for stack to settle to a completed status wait_for_completion(cf_client, stack_name)