def _precheck(config, action): """ Run through preflight checklist before running terraform apply/destroy. Args: config: dictionary containing all variable settings required to run terraform with action: string (create | destroy) Returns: Nothing Raises: InvalidCommandException exception on unknown command. """ # Instantiate remote state only if: # - the terraform code isn't already checked out # - and we haven't already run 'terraform remote config...' if not os.path.isfile( os.path.join(config['tf_root'], '.terraform', 'terraform.tfstate')): log_msg = "Configuring terraform remote state in: {}" logger.debug(log_msg.format(config['tf_root'])) tf_command = tf.remote_state(config) return_code = utils.run_command(tf_command, cwd=config['tf_root']) if return_code != 0: raise BaseException("{} failed with code {}.".format( tf_command, return_code)) # Grab all the tf modules required tf_command = tf.get() utils.run_command(tf_command, cwd=config['tf_root']) # validate env_name utils.validate_env_name(config['env_name']) tf_command = tf.validate(config) utils.run_command(tf_command, cwd=config['tf_root']) # Push remote state push_or_pull = { 'create': tf.remote_push, 'plan': tf.remote_pull, 'destroy': tf.remote_pull, } try: tf_command = push_or_pull[action]() except KeyError: raise InvalidCommandException("Invalid Command: {}".format(action)) utils.run_command(tf_command, cwd=config['tf_root']) # Run Plan tf_command = tf.plan(config, action) utils.run_command(tf_command, cwd=config['tf_root']) return
def test_validate_env_name_length(): good_name = 'thisisalongenvname-a' bad_name = 'thisenvnameistoolong-a' assert utils.validate_env_name(good_name) except_msg = "Environment Name too long: {} characters. Limit: 20." with pytest.raises(EnvironmentNameException) as e: utils.validate_env_name(bad_name) assert (e.value.message == (except_msg.format(len(bad_name))))
def test_validate_env_name_version_is_char(): good_name = 'env-a' bad_name_double_char = 'env-aa' bad_name_number = 'env-1' assert utils.validate_env_name(good_name) except_msg = "Environment version is limited to a single character: {}" msg_double_char = except_msg.format(bad_name_double_char.split('-')[-1]) msg_number = except_msg.format(bad_name_number.split('-')[-1]) with pytest.raises(EnvironmentNameException) as e: utils.validate_env_name(bad_name_double_char) assert (e.value.message == msg_double_char) with pytest.raises(EnvironmentNameException) as e: #pytest.set_trace() utils.validate_env_name(bad_name_number) assert (e.value.message == msg_number)
def test_validate_env_name_num_tokens(): shared_env_name = 'env' ephemeral_env_name = 'env-a' too_many_tokens = 'test-env-a' too_few_tokens = '' assert utils.validate_env_name(shared_env_name) assert utils.validate_env_name(ephemeral_env_name) except_msg = "Incorrect number of tokens. " except_msg += "Environment name can not be undefined" with pytest.raises(EnvironmentNameException) as e: utils.validate_env_name(too_few_tokens) assert (e.value.message == except_msg) except_msg = "Incorrect number of tokens. Should be 1 or 2 tokens, not {}" with pytest.raises(EnvironmentNameException) as e: utils.validate_env_name(too_many_tokens) assert (e.value.message == except_msg.format( len(too_many_tokens.split('-'))))
def configure(config): """ Configure the environment for both Terraform & boto3 to operate via the AWS API. Args: config: dictionary containing all variable settings required to run terraform with Returns: config """ # Clear out AWS_* vars from the environment if they exist try: os.environ.pop('AWS_DEFAULT_PROFILE') os.environ.pop('AWS_SECRET_ACCESS_KEY') os.environ.pop('AWS_ACCESS_KEY_ID') os.environ.pop('AWS_DEFAULT_REGION') except KeyError: # They weren't set, so move on pass # Set the default profile os.environ['AWS_DEFAULT_PROFILE'] = config['aws_profile'] os.environ['AWS_PROFILE'] = config['aws_profile'] os.environ['AWS_DEFAULT_REGION'] = config['aws_region'] if os.environ.get('API_TOKEN') and "API_TOKEN" in config.get('terraform'): git_url = config['terraform'] config['terraform'] = git_url.replace("API_TOKEN", os.environ['API_TOKEN']) config['API_TOKEN'] = os.environ['API_TOKEN'] boto3.setup_default_session(profile_name=config['aws_profile']) boto_profile = get_account_name() # Verify the profile name we get back from AWS is the same as we just set # otherwise exit now. try: assert (os.environ.get('AWS_DEFAULT_PROFILE') == config['aws_profile']) assert (config['aws_profile'] == boto_profile) assert (os.environ.get('AWS_DEFAULT_PROFILE') == boto_profile) except AssertionError: msg = "AWS_DEFAULT_PROFILE mismatch with reality.\n" msg += "\t AWS_DEFAULT_PROFILE: {}\n" msg += "\t Profile passed in from config: {}\n" msg += "\t Profile boto3 thinks we're using: {}\n" logger.critical( msg.format(os.environ.get('AWS_DEFAULT_PROFILE'), config['aws_profile'], boto_profile)) raise config['availability_zones'] = get_current_az_list(config) config['account_id'] = config.get('account_id', get_account_id()) env_name = "{}".format(config['environment'].get('name')) if config['environment'].get('version'): env_name = "{}-{}".format(config['environment']['name'], config['environment']['version']) # Add env_name and env_version to tags. if 'tags' in config: if 'name' in config['environment']: config['tags']['env_name'] = config['environment'].get('name') if 'version' in config['environment']: config['tags']['env_version'] = config['environment'].get( 'version') # Figure out where our buckets are. This should go in pre-flight, # but we haven't established our account_id yet, therefore needs # to be here, after we set up the aws stuff. config['project_config'] = config.get( 'project_config', deployer.s3.get_bucket_name(config, 'data')) config['env_folder'] = config.get('env_folder', env_name) tf_state_bucket_name = deployer.s3.get_bucket_name(config, "tfstate") config['tf_state_bucket'] = config.get('tf_state_bucket', tf_state_bucket_name) tf_state = "{}.tfstate".format(env_name) config['tf_state'] = config.get('tf_state', tf_state) config['env_name'] = config.get('env_name', env_name) utils.validate_env_name(config['env_name']) config['route53_tld'] = config.get('route53_tld') return config