diff --git a/conf/global.json b/conf/global.json index a4b2edb33..fd14ad531 100644 --- a/conf/global.json +++ b/conf/global.json @@ -5,6 +5,7 @@ "region": "us-east-1" }, "general": { + "terraform_files": [], "matcher_locations": [ "matchers" ], diff --git a/docs/source/config-global.rst b/docs/source/config-global.rst index 496eac927..9f647bca6 100644 --- a/docs/source/config-global.rst +++ b/docs/source/config-global.rst @@ -61,6 +61,9 @@ Configuration { "general": { + "terraform_files": [ + "/absolute/path/to/extra/terraform/file.tf" + ], "matcher_locations": [ "matchers" ], @@ -90,6 +93,7 @@ Options ``scheduled_query_locations`` Yes ``["scheduled_queries"]`` List of local paths where ``scheduled_queries`` are defined ``publisher_locations`` Yes ``["publishers"]`` List of local paths where ``publishers`` are defined ``third_party_libraries`` No ``["pathlib2==2.3.5"]`` List of third party dependencies that should be installed via ``pip`` at deployment time. These are libraries needed in rules, custom code, etc that are defined in one of the above settings. +``terraform_files`` No ``[]`` List of local paths to Terraform files that should be included as part of this StreamAlert deployment ============================= ============= ========================= =============== diff --git a/manage.py b/manage.py index fe6c5a16f..94155ea78 100755 --- a/manage.py +++ b/manage.py @@ -23,13 +23,17 @@ terraform """ -from argparse import ArgumentParser, RawDescriptionHelpFormatter +from argparse import ArgumentParser, FileType, RawDescriptionHelpFormatter import sys from streamalert import __version__ as version from streamalert_cli.config import DEFAULT_CONFIG_PATH from streamalert_cli.runner import cli_runner, StreamAlertCLICommandRepository -from streamalert_cli.utils import DirectoryType, generate_subparser +from streamalert_cli.utils import ( + DirectoryType, + generate_subparser, + UniqueSortedFileListAppendAction, +) def build_parser(): @@ -79,6 +83,29 @@ def build_parser(): type=DirectoryType() ) + parser.add_argument( + '-t', + '--terraform-file', + dest='terraform_files', + help=( + 'Path to one or more additional Terraform configuration ' + 'files to include in this deployment' + ), + action=UniqueSortedFileListAppendAction, + type=FileType('r'), + default=[] + ) + + parser.add_argument( + '-b', + '--build-directory', + help=( + 'Path to directory to use for building StreamAlert and its infrastructure. ' + 'If no path is provided, a temporary directory will be used.' + ), + type=str + ) + # Dynamically generate subparsers, and create a 'commands' block for the prog description command_block = [] subparsers = parser.add_subparsers(dest='command', required=True) diff --git a/streamalert_cli/config.py b/streamalert_cli/config.py index f98a6eafb..f3029728b 100644 --- a/streamalert_cli/config.py +++ b/streamalert_cli/config.py @@ -16,7 +16,9 @@ import json import os import re +import shutil import string +import tempfile from streamalert.apps import StreamAlertApp from streamalert.shared import CLUSTERED_FUNCTIONS, config, metrics @@ -32,9 +34,11 @@ class CLIConfig: """A class to load, modify, and display the StreamAlertCLI Config""" - def __init__(self, config_path): + def __init__(self, config_path, extra_terraform_files=None, build_directory=None): self.config_path = config_path self.config = config.load_config(config_path) + self._terraform_files = extra_terraform_files or [] + self.build_directory = self._setup_build_directory(build_directory) def __repr__(self): return str(self.config) @@ -58,6 +62,32 @@ def clusters(self): """Return list of cluster configuration keys""" return list(self.config['clusters'].keys()) + @property + def terraform_files(self): + """Return set of terraform files to include with this deployment""" + return set(self._terraform_files).union( + self.config['global']['general'].get('terraform_files', []) + ) + + @staticmethod + def _setup_build_directory(directory): + """Create the directory to be used for building infrastructure + + Args: + directory (str): Optional path to directory to create + + Returns: + str: Path to directory that will be used + """ + if not directory: + temp_dir = tempfile.TemporaryDirectory(prefix='streamalert_build-') + directory = temp_dir.name + + if os.path.exists(directory): + shutil.rmtree(directory) + + return directory + def set_prefix(self, prefix): """Set the Org Prefix in Global settings""" if not isinstance(prefix, str): diff --git a/streamalert_cli/helpers.py b/streamalert_cli/helpers.py index 32f2f429c..b5c9339bc 100644 --- a/streamalert_cli/helpers.py +++ b/streamalert_cli/helpers.py @@ -24,8 +24,6 @@ from streamalert.shared.logger import get_logger -from streamalert_cli.terraform import TERRAFORM_FILES_PATH - LOGGER = get_logger(__name__) @@ -39,7 +37,7 @@ } -def run_command(runner_args, **kwargs): +def run_command(runner_args, cwd='./', **kwargs): """Helper function to run commands with error handling. Args: @@ -52,7 +50,6 @@ def run_command(runner_args, **kwargs): """ default_error_message = "An error occurred while running: {}".format(' '.join(runner_args)) error_message = kwargs.get('error_message', default_error_message) - cwd = kwargs.get('cwd', TERRAFORM_FILES_PATH) # Add the -force-copy flag for s3 state copying to suppress dialogs that # the user must type 'yes' into. @@ -98,12 +95,13 @@ def continue_prompt(message=None): return response == 'yes' -def tf_runner(action='apply', refresh=True, auto_approve=False, targets=None): +def tf_runner(config, action='apply', refresh=True, auto_approve=False, targets=None): """Terraform wrapper to build StreamAlert infrastructure. Resolves modules with `terraform get` before continuing. Args: + config (CLIConfig): Loaded StreamAlert config 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. @@ -113,8 +111,12 @@ def tf_runner(action='apply', refresh=True, auto_approve=False, targets=None): Returns: bool: True if the terraform command was successful """ + LOGGER.info('Initializing StreamAlert') + if not run_command(['terraform', 'init'], cwd=config.build_directory): + return False + LOGGER.debug('Resolving Terraform modules') - if not run_command(['terraform', 'get'], quiet=True): + if not run_command(['terraform', 'get'], cwd=config.build_directory, quiet=True): return False tf_command = ['terraform', action, '-refresh={}'.format(str(refresh).lower())] @@ -130,7 +132,7 @@ def tf_runner(action='apply', refresh=True, auto_approve=False, targets=None): if targets: tf_command.extend('-target={}'.format(x) for x in targets) - return run_command(tf_command) + return run_command(tf_command, cwd=config.build_directory) def check_credentials(): diff --git a/streamalert_cli/kinesis/handler.py b/streamalert_cli/kinesis/handler.py index 357a0ffe9..80d02d3e0 100644 --- a/streamalert_cli/kinesis/handler.py +++ b/streamalert_cli/kinesis/handler.py @@ -83,6 +83,7 @@ def handler(cls, options, config): return False return tf_runner( + config, action='apply', targets=[ 'module.{}_{}'.format('kinesis_events', cluster) for cluster in config.clusters() diff --git a/streamalert_cli/manage_lambda/deploy.py b/streamalert_cli/manage_lambda/deploy.py index c74808145..1da66ced8 100644 --- a/streamalert_cli/manage_lambda/deploy.py +++ b/streamalert_cli/manage_lambda/deploy.py @@ -124,7 +124,7 @@ def deploy(config, functions, clusters=None): LOGGER.debug('Applying terraform targets: %s', ', '.join(sorted(deploy_targets))) # Terraform applies the new package and publishes a new version - return helpers.tf_runner(targets=deploy_targets) + return helpers.tf_runner(config, targets=deploy_targets) def _update_rule_table(options, config): diff --git a/streamalert_cli/manage_lambda/package.py b/streamalert_cli/manage_lambda/package.py index 70bb75a73..9e471970c 100644 --- a/streamalert_cli/manage_lambda/package.py +++ b/streamalert_cli/manage_lambda/package.py @@ -19,7 +19,6 @@ from streamalert.shared.logger import get_logger from streamalert_cli.helpers import run_command -from streamalert_cli.terraform import TERRAFORM_FILES_PATH LOGGER = get_logger(__name__) @@ -29,7 +28,7 @@ class LambdaPackage: # The name of the directory to package and basename of the generated .zip file PACKAGE_NAME = 'streamalert' - # The configurable items for user specified files + # The configurable items for user specified files to include in deployment pacakge CONFIG_EXTRAS = { 'matcher_locations', 'rule_locations', @@ -86,7 +85,7 @@ def create(self): # Zip it all up # Build these in the top-level of the terraform directory as streamalert.zip result = shutil.make_archive( - os.path.join(TERRAFORM_FILES_PATH, self.PACKAGE_NAME), + os.path.join(self.config.build_directory, self.PACKAGE_NAME), 'zip', self.temp_package_path ) diff --git a/streamalert_cli/runner.py b/streamalert_cli/runner.py index f13dd88f9..0219c5e18 100644 --- a/streamalert_cli/runner.py +++ b/streamalert_cli/runner.py @@ -34,7 +34,6 @@ from streamalert_cli.terraform.generate import TerraformGenerateCommand from streamalert_cli.terraform.handlers import ( TerraformBuildCommand, - TerraformCleanCommand, TerraformDestroyCommand, TerraformInitCommand, TerraformListTargetsCommand, @@ -59,7 +58,7 @@ def cli_runner(args): Returns: bool: False if errors occurred, True otherwise """ - config = CLIConfig(args.config_dir) + config = CLIConfig(args.config_dir, args.terraform_files, args.build_directory) set_logger_levels(args.debug) @@ -93,7 +92,6 @@ def register_all(cls): 'app': AppCommand, 'athena': AthenaCommand, 'build': TerraformBuildCommand, - 'clean': TerraformCleanCommand, 'configure': ConfigureCommand, 'create-alarm': CreateMetricAlarmCommand, 'create-cluster-alarm': CreateClusterMetricAlarmCommand, diff --git a/streamalert_cli/terraform/generate.py b/streamalert_cli/terraform/generate.py index ab2c21449..a73c219e4 100644 --- a/streamalert_cli/terraform/generate.py +++ b/streamalert_cli/terraform/generate.py @@ -13,9 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. """ -from fnmatch import fnmatch import json import os +import shutil from streamalert.shared.config import ConfigError, firehose_alerts_bucket from streamalert.shared.logger import get_logger @@ -373,14 +373,6 @@ def generate_cluster(config, cluster_name): return cluster_dict -def cleanup_old_tf_files(): - """ - Cleanup old *.tf.json files - """ - for terraform_file in os.listdir(TERRAFORM_FILES_PATH): - if fnmatch(terraform_file, '*.tf.json'): - os.remove(os.path.join(TERRAFORM_FILES_PATH, terraform_file)) - class TerraformGenerateCommand(CLICommand): description = 'Generate Terraform files from JSON cluster files' @@ -394,6 +386,28 @@ def handler(cls, options, config): return terraform_generate_handler(config, check_creds=False) +def _copy_terraform_files(config): + """Copy all packaged terraform files and terraform files provided by the user to temp + + Args: + config (CLIConfig): Loaded StreamAlert config + """ + # Copy the packaged terraform files to temp + # Currently this ignores *.tf.json and *.zip files, in the instance that these + # exist in current deployments. This can be removed in a future release. + shutil.copytree( + TERRAFORM_FILES_PATH, + config.build_directory, + ignore=shutil.ignore_patterns('*.tf.json', '*.zip') # TODO: remove this eventually + ) + + # Copy any additional user provided terraform files to temp + for item in config.terraform_files: + shutil.copy2(item, config.build_directory) + + LOGGER.info('Copied Terraform configuration to \'%s\'', config.build_directory) + + def terraform_generate_handler(config, init=False, check_tf=True, check_creds=True): """Generate all Terraform plans for the configured clusters. @@ -412,13 +426,13 @@ def terraform_generate_handler(config, init=False, check_tf=True, check_creds=Tr if check_tf and not terraform_check(): return False - cleanup_old_tf_files() + _copy_terraform_files(config) # Setup the main.tf.json file LOGGER.debug('Generating cluster file: main.tf.json') _create_terraform_module_file( generate_main(config, init=init), - os.path.join(TERRAFORM_FILES_PATH, 'main.tf.json') + os.path.join(config.build_directory, 'main.tf.json') ) # Setup Artifact Extractor if it is enabled. @@ -451,21 +465,21 @@ def terraform_generate_handler(config, init=False, check_tf=True, check_creds=Tr file_name = '{}.tf.json'.format(cluster) _create_terraform_module_file( cluster_dict, - os.path.join(TERRAFORM_FILES_PATH, file_name), + os.path.join(config.build_directory, file_name), ) metric_filters = generate_aggregate_cloudwatch_metric_filters(config) if metric_filters: _create_terraform_module_file( metric_filters, - os.path.join(TERRAFORM_FILES_PATH, 'metric_filters.tf.json') + os.path.join(config.build_directory, 'metric_filters.tf.json') ) metric_alarms = generate_aggregate_cloudwatch_metric_alarms(config) if metric_alarms: _create_terraform_module_file( metric_alarms, - os.path.join(TERRAFORM_FILES_PATH, 'metric_alarms.tf.json') + os.path.join(config.build_directory, 'metric_alarms.tf.json') ) # Setup Threat Intel Downloader Lambda function if it is enabled @@ -531,7 +545,7 @@ def _generate_lookup_tables_settings(config): """ Generates .tf.json file for LookupTables """ - tf_file_name = os.path.join(TERRAFORM_FILES_PATH, 'lookup_tables.tf.json') + tf_file_name = os.path.join(config.build_directory, 'lookup_tables.tf.json') if not config['lookup_tables'].get('enabled', False): remove_temp_terraform_file(tf_file_name) @@ -594,7 +608,7 @@ def _generate_streamquery_module(config): """ Generates .tf.json file for scheduled queries """ - tf_file_name = os.path.join(TERRAFORM_FILES_PATH, 'scheduled_queries.tf.json') + tf_file_name = os.path.join(config.build_directory, 'scheduled_queries.tf.json') if not config.get('scheduled_queries', {}).get('enabled', False): remove_temp_terraform_file(tf_file_name) return @@ -637,7 +651,7 @@ def generate_global_lambda_settings( ) raise ConfigError(message) - tf_tmp_file = os.path.join(TERRAFORM_FILES_PATH, '{}.tf.json'.format(tf_tmp_file_name)) + tf_tmp_file = os.path.join(config.build_directory, '{}.tf.json'.format(tf_tmp_file_name)) if required and conf_name not in config['lambda']: message = 'Required configuration missing in lambda.json: {}'.format(conf_name) diff --git a/streamalert_cli/terraform/handlers.py b/streamalert_cli/terraform/handlers.py index ba91777a6..e96426dee 100644 --- a/streamalert_cli/terraform/handlers.py +++ b/streamalert_cli/terraform/handlers.py @@ -16,7 +16,6 @@ from fnmatch import fnmatch import json import os -import shutil from streamalert.shared.config import firehose_alerts_bucket from streamalert.shared.logger import get_logger @@ -24,7 +23,6 @@ from streamalert_cli.athena.handler import create_table, create_log_tables from streamalert_cli.helpers import check_credentials, continue_prompt, run_command, tf_runner from streamalert_cli.manage_lambda.deploy import deploy -from streamalert_cli.terraform import TERRAFORM_FILES_PATH from streamalert_cli.terraform.generate import terraform_generate_handler from streamalert_cli.terraform.helpers import terraform_check from streamalert_cli.utils import ( @@ -42,16 +40,7 @@ class TerraformInitCommand(CLICommand): @classmethod def setup_subparser(cls, subparser): - """Add init subparser: manage.py init [options]""" - subparser.add_argument( - '-b', - '--backend', - action='store_true', - help=( - 'Initialize the Terraform backend (S3). ' - 'Useful for refreshing a pre-existing deployment' - ) - ) + """Manage.py init takes no arguments""" @classmethod def handler(cls, options, config): @@ -63,11 +52,6 @@ def handler(cls, options, config): Returns: bool: False if errors occurred, True otherwise """ - - # Stop here if only initializing the backend - if options.backend: - return cls._terraform_init_backend(config) - LOGGER.info('Initializing StreamAlert') # generate init Terraform files @@ -75,7 +59,7 @@ def handler(cls, options, config): return False LOGGER.info('Initializing Terraform') - if not run_command(['terraform', 'init']): + if not run_command(['terraform', 'init'], cwd=config.build_directory): return False # build init infrastructure @@ -95,7 +79,7 @@ def handler(cls, options, config): if config['global']['infrastructure'].get('firehose', {}).get('enabled'): init_targets.append('aws_s3_bucket.streamalert_data') - if not tf_runner(targets=init_targets): + if not tf_runner(config, targets=init_targets): LOGGER.error('An error occurred while running StreamAlert init') return False @@ -104,7 +88,7 @@ def handler(cls, options, config): if not terraform_generate_handler(config=config, check_tf=False, check_creds=False): return False - if not run_command(['terraform', 'init']): + if not run_command(['terraform', 'init'], cwd=config.build_directory): return False LOGGER.info('Deploying Lambda Functions') @@ -131,29 +115,7 @@ def handler(cls, options, config): return LOGGER.info('Building remaining infrastructure') - return tf_runner(refresh=False) - - @staticmethod - def _terraform_init_backend(config): - """Initialize the infrastructure backend (S3) using Terraform - - Returns: - bool: False if errors occurred, True otherwise - """ - # Check for valid credentials - if not check_credentials(): - return False - - # Verify terraform is installed - if not terraform_check(): - return False - - # See generate_main() for how it uses the `init` kwarg for the local/remote backend - if not terraform_generate_handler(config=config, init=False): - return False - - LOGGER.info('Initializing StreamAlert backend') - return run_command(['terraform', 'init']) + return tf_runner(config, refresh=False) class TerraformBuildCommand(CLICommand): @@ -199,7 +161,7 @@ def handler(cls, options, config): if not valid: return False - return tf_runner(targets=target_modules if target_modules else None) + return tf_runner(config, targets=target_modules if target_modules else None) class TerraformDestroyCommand(CLICommand): @@ -250,6 +212,7 @@ def handler(cls, options, config): return False return tf_runner( + config, action='destroy', auto_approve=True, targets=target_modules if target_modules else None @@ -262,58 +225,11 @@ def handler(cls, options, config): check_creds=False): return False - if not run_command(['terraform', 'init']): + if not run_command(['terraform', 'init'], cwd=config.build_directory): return False # Destroy all of the infrastructure - if not tf_runner(action='destroy', auto_approve=True): - return False - - # Remove old Terraform files - return TerraformCleanCommand.handler(options, config) - - -class TerraformCleanCommand(CLICommand): - description = 'Remove current Terraform files' - - @classmethod - def setup_subparser(cls, subparser): - """Manage.py clean takes no arguments""" - - @classmethod - def handler(cls, options, config): - """Remove leftover Terraform statefiles and main/cluster files - - Args: - config (CLIConfig): Loaded StreamAlert config - - Returns: - bool: False if errors occurred, True otherwise - """ - LOGGER.info('Cleaning Terraform files') - - def _rm_file(path): - if not os.path.isfile(path): - return - print('Removing terraform file: {}'.format(path)) - os.remove(path) - - for root, _, files in os.walk(TERRAFORM_FILES_PATH): - for file_name in files: - path = os.path.join(root, file_name) - if path.endswith('.tf.json'): - _rm_file(path) - - for tf_file in ['terraform.tfstate', 'terraform.tfstate.backup']: - path = os.path.join(TERRAFORM_FILES_PATH, tf_file) - _rm_file(path) - - # Finally, delete the Terraform directory - tf_path = os.path.join(TERRAFORM_FILES_PATH, '.terraform') - if os.path.isdir(tf_path): - shutil.rmtree(tf_path) - - return True + return tf_runner(config, action='destroy', auto_approve=True) class TerraformListTargetsCommand(CLICommand): @@ -409,7 +325,7 @@ def get_tf_modules(config, generate=False): modules = set() resources = set() - for root, _, files in os.walk(TERRAFORM_FILES_PATH): + for root, _, files in os.walk(config.build_directory): for file_name in files: path = os.path.join(root, file_name) if path.endswith('.tf.json'): diff --git a/streamalert_cli/terraform/helpers.py b/streamalert_cli/terraform/helpers.py index c22752e75..536e3dd93 100644 --- a/streamalert_cli/terraform/helpers.py +++ b/streamalert_cli/terraform/helpers.py @@ -22,8 +22,14 @@ def terraform_check(): """Verify that Terraform is configured correctly Returns: - bool: Success or failure of the command ran""" - prereqs_message = ('Terraform not found! Please install and add to ' - 'your $PATH:\n' - '\t$ export PATH=$PATH:/usr/local/terraform/bin') - return run_command(['terraform', 'version'], error_message=prereqs_message, quiet=True) + bool: Success or failure of the command ran + """ + error_message = ( + 'Terraform not found! Please install and add to your $PATH:\n' + '\texport PATH=$PATH:/usr/local/terraform/bin' + ) + return run_command( + ['terraform', 'version'], + error_message=error_message, + quiet=True, + ) diff --git a/streamalert_cli/utils.py b/streamalert_cli/utils.py index a9540af74..baa8602c6 100644 --- a/streamalert_cli/utils.py +++ b/streamalert_cli/utils.py @@ -24,7 +24,7 @@ terraform """ from abc import abstractmethod -from argparse import Action, ArgumentTypeError, RawDescriptionHelpFormatter +from argparse import _AppendAction, Action, ArgumentTypeError, RawDescriptionHelpFormatter import os import textwrap from streamalert.apps.config import AWS_RATE_RE, AWS_RATE_HELPER @@ -93,6 +93,18 @@ def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, sorted(unique_items)) # We want this to be consistent +class UniqueSortedFileListAppendAction(_AppendAction): + """Subclass of argparse._AppendAction to avoid multiple of the same choice from a list of files + + This is meant to augment the 'append' argparse action + """ + + def __call__(self, parser, namespace, value, option_string=None): + unique_items = set(getattr(namespace, self.dest, set())) + unique_items.add(value.name) + setattr(namespace, self.dest, sorted(unique_items)) # We want this to be consistent + + class MutuallyExclusiveStagingAction(Action): """Subclass of argparse.Action to avoid staging and unstaging the same rules""" diff --git a/tests/unit/conf/global.json b/tests/unit/conf/global.json index cedda3d39..df375c40d 100644 --- a/tests/unit/conf/global.json +++ b/tests/unit/conf/global.json @@ -6,6 +6,7 @@ "region": "us-west-1" }, "general": { + "terraform_files": [], "matcher_locations": [], "rule_locations": [], "scheduled_query_locations": [], diff --git a/tests/unit/helpers/config.py b/tests/unit/helpers/config.py index 9e7bf0362..2538a8e8a 100644 --- a/tests/unit/helpers/config.py +++ b/tests/unit/helpers/config.py @@ -21,6 +21,7 @@ class MockCLIConfig: def __init__(self, config): self.config = config + self.build_directory = 'streamalert_terraform_unit_test' def __repr__(self): return json.dumps(self.config) @@ -50,6 +51,11 @@ def basic_streamalert_config(): 'prefix': 'unit-test', 'region': 'us-west-2' }, + 'general': { + 'terraform_files': [ + '/test/terraform/file.tf' + ] + }, 'infrastructure': { 'monitoring': {}, 's3_access_logging': { diff --git a/tests/unit/streamalert_cli/manage_lambda/test_package.py b/tests/unit/streamalert_cli/manage_lambda/test_package.py index 182121d88..d1d1bf2b3 100644 --- a/tests/unit/streamalert_cli/manage_lambda/test_package.py +++ b/tests/unit/streamalert_cli/manage_lambda/test_package.py @@ -32,9 +32,11 @@ def setUp(self): self.setUpPyfakefs() self.fs.add_real_directory(self.TEST_CONFIG_PATH) + config = CLIConfig(self.TEST_CONFIG_PATH) + with patch('tempfile.gettempdir') as temp_dir_mock: temp_dir_mock.return_value = self.MOCK_TEMP_PATH - self.packager = package.LambdaPackage(CLIConfig(self.TEST_CONFIG_PATH)) + self.packager = package.LambdaPackage(config) def test_copy_directory_destination(self): """CLI - LambdaPackage copy directory using destination""" diff --git a/tests/unit/streamalert_cli/terraform/test_handlers.py b/tests/unit/streamalert_cli/terraform/test_handlers.py index 2bd382f8c..c529e46aa 100644 --- a/tests/unit/streamalert_cli/terraform/test_handlers.py +++ b/tests/unit/streamalert_cli/terraform/test_handlers.py @@ -20,7 +20,6 @@ from nose.tools import assert_equal, assert_false from pyfakefs import fake_filesystem_unittest -from streamalert_cli.terraform import TERRAFORM_FILES_PATH from streamalert_cli.terraform.handlers import get_tf_modules class TestTerraformHandlers(fake_filesystem_unittest.TestCase): @@ -30,6 +29,7 @@ class TestTerraformHandlers(fake_filesystem_unittest.TestCase): def setUp(self): """Setup before each method""" self.setUpPyfakefs() + self._build_directory = 'unit_test_terraform_path' mock_main_tf_json = { 'module': { @@ -52,18 +52,18 @@ def setUp(self): } # fake *.tf.json files self.fs.create_file( - os.path.join(TERRAFORM_FILES_PATH, 'main.tf.json'), + os.path.join(self._build_directory, 'main.tf.json'), contents=json.dumps(mock_main_tf_json) ) self.fs.create_file( - os.path.join(TERRAFORM_FILES_PATH, 'prod.tf.json'), + os.path.join(self._build_directory, 'prod.tf.json'), contents=json.dumps(mock_prod_tf_json) ) @patch('streamalert_cli.terraform.handlers.terraform_generate_handler', Mock(return_value=True)) def test_get_tf_modules_read_tf_json_files(self): """CLI - Terraform handler function get tf modules read all *.tf.json files""" - config = {} + config = Mock(return_value={}, build_directory=self._build_directory) result = get_tf_modules(config) expected_result = { @@ -78,4 +78,5 @@ def test_get_tf_modules_read_tf_json_files(self): ) def test_get_tf_modules_early_return(self): """CLI - Terraform handler function get tf modules return early""" - assert_false(get_tf_modules(config={}, generate=True)) + config = Mock(return_value={}, build_directory=self._build_directory) + assert_false(get_tf_modules(config, generate=True)) diff --git a/tests/unit/streamalert_cli/test_cli_config.py b/tests/unit/streamalert_cli/test_cli_config.py index 50cfc8edc..aa9662711 100644 --- a/tests/unit/streamalert_cli/test_cli_config.py +++ b/tests/unit/streamalert_cli/test_cli_config.py @@ -61,6 +61,10 @@ def test_load_config(self): """CLI - Load config""" assert_equal(self.config['global']['account']['prefix'], 'unit-test') + def test_terraform_files(self): + """CLI - Terraform Files""" + assert_equal(self.config.terraform_files, {'/test/terraform/file.tf'}) + def test_toggle_metric(self): """CLI - Metric toggling""" self.config.toggle_metrics('athena_partitioner', enabled=True)