Skip to content

Commit

Permalink
Speed up deploys with -auto-approve (#604)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinbyers authored and ryandeivert committed Mar 14, 2018
1 parent 9ec0839 commit 78785df
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 45 deletions.
62 changes: 21 additions & 41 deletions stream_alert_cli/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import re
from StringIO import StringIO
import subprocess
import sys
import zipfile
import zlib

Expand All @@ -40,6 +39,7 @@
from stream_alert_cli.logger import LOGGER_CLI
from stream_alert.rule_processor.firehose import StreamAlertFirehose


def run_command(runner_args, **kwargs):
"""Helper function to run commands with error handling.
Expand Down Expand Up @@ -102,62 +102,40 @@ def continue_prompt(message=''):
return response == 'yes'


def tf_runner(**kwargs):
def tf_runner(action='apply', refresh=True, auto_approve=False, targets=None):
"""Terraform wrapper to build StreamAlert infrastructure.
Steps:
- resolve modules with `terraform get`
- run `terraform plan` for the given targets
- if plan is successful and user confirms prompt,
then the infrastructure is applied
Resolves modules with `terraform get` before continuing.
kwargs:
targets: a list of Terraform targets
action: 'apply' or 'destroy'
Args:
action (str): Terraform action ('apply' or 'destroy').
refresh (bool): If True, Terraform will refresh its state before applying the change.
auto_approve (bool): If True, Terraform will *not* prompt the user for approval.
targets (list): Optional list of affected targets.
If not specified, Terraform will run against all of its resources.
Returns:
bool: True if the terraform command was successful
"""
targets = kwargs.get('targets', [])
action = kwargs.get('action', None)
tf_action_index = 1 # The index to the terraform 'action'

var_files = {'conf/lambda.json'}
tf_opts = ['-var-file=../{}'.format(x) for x in var_files]
tf_targets = ['-target={}'.format(x) for x in targets]
tf_command = ['terraform', 'plan'] + tf_opts + tf_targets
if action == 'destroy':
tf_command.append('-destroy')

LOGGER_CLI.debug('Resolving Terraform modules')
if not run_command(['terraform', 'get'], quiet=True):
return False

LOGGER_CLI.info('Planning infrastructure')
if not run_command(tf_command):
return False

if not continue_prompt():
sys.exit(1)
tf_command = ['terraform', action, '-var-file=../conf/lambda.json',
'-refresh={}'.format(str(refresh).lower())]

if action == 'destroy':
# Terraform destroy has a '-force' flag instead of '-auto-approve'
LOGGER_CLI.info('Destroying infrastructure')
tf_command[tf_action_index] = action
tf_command.remove('-destroy')
tf_command.append('-force')

elif action:
tf_command[tf_action_index] = action

tf_command.append('-force={}'.format(str(auto_approve).lower()))
else:
LOGGER_CLI.info('Creating infrastructure')
tf_command[tf_action_index] = 'apply'
tf_command.append('-refresh=false')
LOGGER_CLI.info('%s changes', 'Applying' if auto_approve else 'Planning')
tf_command.append('-auto-approve={}'.format(str(auto_approve).lower()))

if not run_command(tf_command):
return False
if targets:
tf_command.extend('-target={}'.format(x) for x in targets)

return True
return run_command(tf_command)


def check_credentials():
Expand Down Expand Up @@ -450,6 +428,7 @@ def setup_mock_firehose_delivery_streams(config):
prefix = '{}/'.format(log_type)
create_delivery_stream(region, stream_name, prefix)


@mock_dynamodb2
def setup_mock_dynamodb_ioc_table(config):
"""Mock DynamoDB IOC table for rule testing
Expand Down Expand Up @@ -507,6 +486,7 @@ def setup_mock_dynamodb_ioc_table(config):
TableName=table_name
)


def put_mock_s3_object(bucket, key, data, region):
"""Create a mock AWS S3 object for testing
Expand Down Expand Up @@ -597,6 +577,7 @@ def get_context_from_config(cluster, config):

return context


def user_input(requested_info, mask, input_restrictions):
"""Prompt user for requested information
Expand All @@ -617,7 +598,6 @@ def user_input(requested_info, mask, input_restrictions):

# Restrict having spaces or colons in items (applies to things like
# descriptors, etc)
valid_response = False
if isinstance(input_restrictions, re._pattern_type):
valid_response = input_restrictions.match(response)
if not valid_response:
Expand Down
2 changes: 1 addition & 1 deletion stream_alert_cli/manage_lambda/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,4 @@ def deploy(options, config):
return

# Apply the changes to the Lambda aliases
helpers.tf_runner(targets=deploy_targets)
helpers.tf_runner(targets=deploy_targets, refresh=False, auto_approve=True)
7 changes: 4 additions & 3 deletions stream_alert_cli/terraform/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,15 @@ def terraform_handler(options, config):
# create all remainder infrastructure

LOGGER_CLI.info('Building Remainder Infrastructure')
tf_runner()
tf_runner(refresh=False)

elif options.subcommand == 'clean':
if not continue_prompt(message='Are you sure you want to clean all Terraform files?'):
sys.exit(1)
terraform_clean(config)

elif options.subcommand == 'destroy':
# Ask for approval here since multiple Terraform commands may be necessary
if not continue_prompt(message='Are you sure you want to destroy?'):
sys.exit(1)

Expand All @@ -126,7 +127,7 @@ def terraform_handler(options, config):
targets.extend(
['module.{}_{}'.format(target, cluster) for cluster in config.clusters()])

tf_runner(targets=targets, action='destroy')
tf_runner(action='destroy', auto_approve=True, targets=targets)
return

# Migrate back to local state so Terraform can successfully
Expand All @@ -138,7 +139,7 @@ def terraform_handler(options, config):
return

# Destroy all of the infrastructure
if not tf_runner(action='destroy'):
if not tf_runner(action='destroy', auto_approve=True):
return

# Remove old Terraform files
Expand Down

0 comments on commit 78785df

Please sign in to comment.