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
示例#2
0
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))))
示例#3
0
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)
示例#4
0
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('-'))))
示例#5
0
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