lib/terragov/cli.rb in terragov-0.3.3 vs lib/terragov/cli.rb in terragov-0.4.0

- old
+ new

@@ -4,239 +4,146 @@ require_relative 'buildpaths' require_relative 'terraform' require_relative 'cleaner' require_relative 'version' require_relative 'git' +require_relative 'config' module Terragov class Cli include Commander::Methods def initialize program :name, 'terragov' program :version, Terragov::VERSION program :description, 'Wrapper for GOV.UK Terraform deployments.' + # Project should be loaded independently, unless used in deployment mode + global_option('-p', '--project STRING', String, 'Name of the project') do |project| + $project = project || ENV['TERRAGOV_PROJECT'] + end + global_option('-c', '--config-file FILE', 'Specify a config file. Has less precedence than environment variables, which in turn have less precedence than CLI options') do |config_file| - $config_file = config_file + $config_file = config_file || ENV['TERRAGOV_CONFIG_FILE'] end global_option('-d', '--data-dir DIRECTORY', String, 'Location of the data directory') do |data_dir| - $data_dir = data_dir + @data_dir_cli ||= data_dir end global_option('-e', '--environment STRING', String, 'Select environment') do |environment| - $environment = environment + @environment_cli = environment || false end - global_option('-p', '--project STRING', String, 'Name of the project') do |project| - $project = project - end - global_option('-r', '--repo-dir DIRECTORY', String, 'Location of the main terraform repository') do |repo_dir| - $repo_dir = repo_dir + @repo_dir_cli = repo_dir || false end global_option('-s', '--stack STRING', String, 'Name of the stack') do |stack| - $stack = stack + @stack_cli = stack || false end global_option('--extra STRING', String, 'Any additional arguments to pass in the following format: --extra \\\\-target resource.foo.') do |extra| - $extra = extra + @extra = extra || nil end global_option('--verbose', 'Verbose mode') do |verbose| - $verbose = verbose + @verbose = verbose || false end - global_option('--dry-run', 'Dry run mode', 'Output the commands to run, but do not run them') do |dryrun| - $dryrun = dryrun + global_option('--test', 'Dry run mode', 'Output the commands to run, but do not run them') do |test| + @test = test || false end global_option('--skip-git-check', 'Skip git check', 'Do not check the status of git repositories') do |skip_git_check| - $skip_git_check = skip_git_check + @skip_git_check = skip_git_check || false end end - def data_dir - $data_dir ? $data_dir : false - end - - def environment - $environment ? $environment : false - end - - def project - $project ? $project : false - end - - def repo_dir - $repo_dir ? $repo_dir : false - end - - def stack - $stack ? $stack : false - end - - def extra - return $extra if $extra - end - - def verbose - true ? $verbose : false - end - - def dryrun - true ? $dryrun : false - end - - def skip_git_check - true ? $skip_git_check : false - end - - def load_config_file - if $config_file || ENV['TERRAGOV_CONFIG_FILE'] - file = $config_file || ENV['TERRAGOV_CONFIG_FILE'] - YAML.load_file(File.expand_path(file)) - end - end - - def config_file_default - if load_config_file['default'].nil? - return nil - else - return load_config_file['default'] - end - end - - def config_file_specific_project(project_name) - load_config_file[project_name] - end - - def config_file(option) - # This has to be loaded in seperately to avoid any cyclic dependencies - project_name = $project || ENV['TERRAGOV_PROJECT'] - - if project_name.nil? - if config_file_default.nil? - return nil - else - return config_file_default[option] - end - else - project_config = config_file_specific_project(project_name) - if project_config.nil? or project_config[option].nil? - return config_file_default[option] - else - return project_config[option] - end - end - end - - def config(option, file = false, required = true) - env_var = "TERRAGOV_#{option.upcase}" - error_message = "Must set #{option}. Use --help for details." - - # Load from CLI option - if public_send(option) - if file - return File.expand_path(public_send(option)) - else - return public_send(option) - end - - # Load from environment variable - elsif ENV[env_var] - if file - return File.expand_path(ENV[env_var]) - else - return ENV[env_var] - end - - # Load from config file - elsif !load_config_file.nil? - if config_file(option).nil? - abort(error_message) if required - return false - else - if file - return File.expand_path(config_file(option)) - else - return config_file(option) - end - end - else - abort(error_message) if required - return false - end - end - def cmd_options(deployment = false) cmd_hash = {} # Always load the project name first unless deployment - cmd_hash = { 'project' => config('project') } + cmd_hash = { 'project' => $project } end + config = Terragov::Config.new + + data_dir = config.lookup({ + name: 'data_dir', + cli: @data_dir_cli, + file: true, + }) + environment = config.lookup({ + name: 'environment', + cli: @environment_cli, + }) + repo_dir = config.lookup({ + name: 'repo_dir', + cli: @repo_dir_cli, + file: true, + }) + stack = config.lookup({ + name: 'stack', + cli: @stack_cli, + }) + + #require 'pry'; binding.pry + cmd_hash.merge({ - 'environment' => config('environment'), - 'data_dir' => config('data_dir', true), - 'stack' => config('stack'), - 'repo_dir' => config('repo_dir', true), - 'extra' => extra + 'environment' => environment, + 'data_dir' => data_dir, + 'stack' => stack, + 'repo_dir' => repo_dir, + 'extra' => @extra, }) end def git_compare_repo_and_data(skip = false) - git_helper = Terragov::Git.new - # FIXME: this is confusing as we want to check the repository git status from - # the root, but the "data" directory is one level down in the repository - repo_dir_root = cmd_options['repo_dir'] - data_dir_root = File.expand_path(File.join(cmd_options['data_dir'], '../')) + unless skip + git_helper = Terragov::Git.new + # FIXME: this is confusing as we want to check the repository git status from + # the root, but the "data" directory is one level down in the repository + repo_dir_root = cmd_options['repo_dir'] + data_dir_root = File.expand_path(File.join(cmd_options['data_dir'], '../')) - repo_dir_branch = git_helper.branch_name(repo_dir_root) - data_dir_branch = git_helper.branch_name(data_dir_root) + branches = { + 'repo_dir' => git_helper.branch_name(repo_dir_root), + 'data_dir' => git_helper.branch_name(data_dir_root), + } - branches = { - 'repo_dir' => repo_dir_branch, - 'data_dir' => data_dir_branch - } - - unless skip branches.each do |name, branch| unless branch =~ /^master$/ exit unless HighLine.agree("#{name} not on 'master' branch, continue on branch '#{branch}'?") end end - - unless git_helper.compare_branch(repo_dir_root, data_dir_root) - puts "Warning: repo_dir(#{repo_dir_branch}) and data_dir(#{data_dir_branch}) on different branches" - exit unless HighLine.agree('Do you wish to continue?') - end end end - def run_terraform_cmd(cmd, opt = nil, deployment = false) + def run_terraform_cmd(cmd, deployment = false) + # Create a hash based on method above paths = Terragov::BuildPaths.new.base(cmd_options) - varfiles = Terragov::BuildPaths.new.build_command(cmd_options) - backend = paths[:backend_file] - project_dir = paths[:project_dir] - be_verbose = config('verbose', false, false) + # Construct the hash to pass to the Terraform class + terraform_args = { + command: cmd, + vars: Terragov::BuildPaths.new.build_command(cmd_options), + backend: paths[:backend_file], + directory: paths[:project_dir], + test: Terragov::Config.new.lookup({name: 'test', required: false, cli: @test}), + verbose: Terragov::Config.new.lookup({name: 'verbose', required: false, cli: @verbose}), + } - do_dryrun = config('dryrun', false, false) - unless deployment - skip_check = config('skip_git_check', false, false) + skip_check = Terragov::Config.new.lookup({name: 'skip_git_check', required: false, cli: @skip_git_check}) git_compare_repo_and_data(skip_check) end - puts cmd_options.to_yaml if be_verbose + puts cmd_options.to_yaml if terraform_args[:verbose] - cmd = "#{cmd} #{opt}" if opt - Terragov::Terraform.new.execute(cmd, varfiles, backend, project_dir, do_dryrun, be_verbose) + Terragov::Terraform.new.execute(terraform_args) end def run_deployment(file, group, command, force) abort("Must set deployment file: --file") unless file abort("Must set command to run: --command") unless command @@ -247,27 +154,28 @@ if deployment_config.nil? abort("Deployment configuration must be an array of projects to run") end - if command == 'plan' || command == 'apply' + case command + when 'plan', 'apply' if force && command == 'apply' command = "#{command} -auto-approve" end deployment_config.each do |proj| $project = proj - run_terraform_cmd(command, nil, true) + run_terraform_cmd(command, true) end - elsif command == 'destroy' + when 'destroy' if force command = "#{command} -force" end deployment_config.reverse.each do |proj| $project = proj - run_terraform_cmd(command, nil, true) + run_terraform_cmd(command, true) end else abort("Command must be apply, plan or destroy") end end @@ -325,19 +233,19 @@ command :clean do |c| c.syntax = 'terragov clean' c.description = 'Clean your directory of any files terraform may have left lying around' c.option '--force', 'Force removal of files' c.action do |_args, options| - if config('verbose', false, false) + if Terragov::Config.new.lookup({name: 'verbose', required: false, cli: @verbose}) puts "Selecting directory #{repo_dir}" end files_to_delete = [ /\.terraform$/, /terraform\.tfstate\.backup/, ] - path = config('repo_dir', true) + path = Terragov::Config.new.lookup({name: 'repo_dir', file: true}) Terragov::Cleaner.new.delete(path, files_to_delete, options.force) end end