module MuxTf::Cli::Current

Constants

PLAN_FILENAME

Public Class Methods

run(args) click to toggle source
# File lib/mux_tf/cli/current.rb, line 16
def run(args)
  version_check

  if args[0] == "cli"
    cmd_loop
    return
  end

  folder_name = File.basename(Dir.getwd)
  log "Processing #{Paint[folder_name, :cyan]} ..."

  ENV["TF_IN_AUTOMATION"] = "1"
  ENV["TF_INPUT"] = "0"

  return launch_cmd_loop(:error) unless run_validate

  if ENV["TF_UPGRADE"]
    upgrade_status, upgrade_meta = run_upgrade
    return launch_cmd_loop(:error) unless upgrade_status == :ok
  end

  plan_status, @plan_meta = create_plan(PLAN_FILENAME)

  case plan_status
  when :ok
    log "no changes, exiting", depth: 1
  when :error
    log "something went wrong", depth: 1
    launch_cmd_loop(plan_status)
  when :changes
    log "Printing Plan Summary ...", depth: 1
    pretty_plan_summary(PLAN_FILENAME)
    launch_cmd_loop(plan_status)
  when :unknown
    launch_cmd_loop(plan_status)
  end
rescue Exception => e # rubocop:disable Lint/RescueException
  puts Paint["Unhandled Exception!", :red]
  puts "=" * 20
  puts e.full_message
  puts
  puts "< press enter to continue >"
  gets
  exit 1
end

Private Class Methods

apply_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 177
def apply_cmd
  define_cmd("apply", summary: "Apply the current plan") do |_opts, _args, _cmd|
    status = tf_apply(filename: PLAN_FILENAME)
    if status.success?
      plan_status = run_plan
      throw :stop, :done if plan_status == :ok
    else
      log "Apply Failed!"
    end
  end
end
build_root_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 156
def build_root_cmd
  root_cmd = define_cmd(nil)

  root_cmd.add_command(plan_cmd)
  root_cmd.add_command(apply_cmd)
  root_cmd.add_command(shell_cmd)
  root_cmd.add_command(force_unlock_cmd)
  root_cmd.add_command(upgrade_cmd)
  root_cmd.add_command(reconfigure_cmd)
  root_cmd.add_command(interactive_cmd)

  root_cmd.add_command(exit_cmd)
  root_cmd
end
cmd_loop(status = nil) click to toggle source
# File lib/mux_tf/cli/current.rb, line 134
def cmd_loop(status = nil)
  root_cmd = build_root_cmd

  folder_name = File.basename(Dir.getwd)

  puts root_cmd.help

  prompt = "#{folder_name} => "
  case status
  when :error, :unknown
    prompt = "[#{Paint[status.to_s, :red]}] #{prompt}"
  when :changes
    prompt = "[#{Paint[status.to_s, :yellow]}] #{prompt}"
  end

  run_cmd_loop(prompt) do |cmd|
    throw(:stop, :no_input) if cmd == ""
    args = Shellwords.split(cmd)
    root_cmd.run(args, {}, hard_exit: false)
  end
end
create_plan(filename, targets: []) click to toggle source
# File lib/mux_tf/cli/current.rb, line 106
def create_plan(filename, targets: [])
  log "Preparing Plan ...", depth: 1
  exit_code, meta = PlanFormatter.pretty_plan(filename, targets: targets)
  case exit_code
  when 0
    [:ok, meta]
  when 1
    [:error, meta]
  when 2
    [:changes, meta]
  else
    log Paint["terraform plan exited with an unknown exit code: #{exit_code}", :yellow]
    [:unknown, meta]
  end
end
force_unlock_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 197
def force_unlock_cmd
  define_cmd("force-unlock", summary: "Force unlock state after encountering a lock error!") do
    prompt = TTY::Prompt.new(interrupt: :noop)

    table = TTY::Table.new(header: %w[Field Value])
    table << ["Lock ID", @plan_meta["ID"]]
    table << ["Operation", @plan_meta["Operation"]]
    table << ["Who", @plan_meta["Who"]]
    table << ["Created", @plan_meta["Created"]]

    puts table.render(:unicode, padding: [0, 1])

    if @plan_meta && @plan_meta["error"] == "lock"
      done = catch(:abort) {
        if @plan_meta["Operation"] != "OperationTypePlan"
          throw :abort unless prompt.yes?(
            "Are you sure you want to force unlock a lock for operation: #{@plan_meta["Operation"]}",
            default: false
          )
        end

        throw :abort unless prompt.yes?(
          "Are you sure you want to force unlock this lock?",
          default: false
        )

        status = tf_force_unlock(id: @plan_meta["ID"])
        if status.success?
          log "Done!"
        else
          log Paint["Failed with status: #{status}", :red]
        end

        true
      }

      log Paint["Aborted", :yellow] unless done
    else
      log Paint["No lock error or no plan ran!", :red]
    end
  end
end
interactive_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 260
def interactive_cmd
  define_cmd("interactive", summary: "Apply interactively") do |_opts, _args, _cmd|
    plan = PlanSummaryHandler.from_file(PLAN_FILENAME)
    begin
      abort_message = catch :abort do
        plan.run_interactive
      end
      if abort_message
        log Paint["Aborted: #{abort_message}", :red]
      else
        run_plan
      end
    rescue Exception => e
      log e.full_message
      log "Interactive Apply Failed!"
    end
  end
end
launch_cmd_loop(status) click to toggle source
# File lib/mux_tf/cli/current.rb, line 122
def launch_cmd_loop(status)
  return if ENV["NO_CMD"]

  case status
  when :error, :unknown
    log Paint["Dropping to command line so you can fix the issue!", :red]
  when :changes
    log Paint["Dropping to command line so you can review the changes.", :yellow]
  end
  cmd_loop(status)
end
plan_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 171
def plan_cmd
  define_cmd("plan", summary: "Re-run plan") do |_opts, _args, _cmd|
    run_validate && run_plan
  end
end
pretty_plan_summary(filename) click to toggle source
# File lib/mux_tf/cli/current.rb, line 311
def pretty_plan_summary(filename)
  plan = PlanSummaryHandler.from_file(filename)
  plan.flat_summary.each do |line|
    log line, depth: 2
  end
  plan.output_summary.each do |line|
    log line, depth: 2
  end
  log "", depth: 2
  log plan.summary, depth: 2
end
process_remedies(remedies) click to toggle source
# File lib/mux_tf/cli/current.rb, line 80
def process_remedies(remedies)
  if remedies.delete? :init
    log "Running terraform init ...", depth: 2
    remedies = PlanFormatter.init_status_to_remedies(*PlanFormatter.run_tf_init)
    if process_remedies(remedies)
      remedies = PlanFormatter.process_validation(validate)
      return false unless process_remedies(remedies)
    end
  end
  if remedies.delete? :reconfigure
    log "Running terraform init ...", depth: 2
    remedies = PlanFormatter.init_status_to_remedies(*PlanFormatter.run_tf_init(reconfigure: true))
    return false unless process_remedies(remedies)
  end
  unless remedies.empty?
    log "unprocessed remedies: #{remedies.to_a}", depth: 1
    return false
  end
  true
end
reconfigure_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 250
def reconfigure_cmd
  define_cmd("reconfigure", summary: "Reconfigure modules/plguins") do |_opts, _args, _cmd|
    status, meta = PlanFormatter.run_tf_init(reconfigure: true)
    if status != 0
      log meta.inspect unless meta.empty?
      log "Reconfigure Failed!"
    end
  end
end
run_plan(targets: []) click to toggle source
# File lib/mux_tf/cli/current.rb, line 279
def run_plan(targets: [])
  plan_status, @plan_meta = create_plan(PLAN_FILENAME, targets: targets)

  case plan_status
  when :ok
    log "no changes", depth: 1
  when :error
    log "something went wrong", depth: 1
  when :changes
    log "Printing Plan Summary ...", depth: 1
    pretty_plan_summary(PLAN_FILENAME)
  when :unknown
    # nothing
  end
  plan_status
end
run_upgrade() click to toggle source
# File lib/mux_tf/cli/current.rb, line 296
def run_upgrade
  exit_code, meta = PlanFormatter.run_tf_init(upgrade: true)
  case exit_code
  when 0
    [:ok, meta]
  when 1
    [:error, meta]
  # when 2
  #   [:changes, meta]
  else
    log Paint["terraform init upgrade exited with an unknown exit code: #{exit_code}", :yellow]
    [:unknown, meta]
  end
end
run_validate() click to toggle source
# File lib/mux_tf/cli/current.rb, line 75
def run_validate
  remedies = PlanFormatter.process_validation(validate)
  process_remedies(remedies)
end
shell_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 189
def shell_cmd
  define_cmd("shell", summary: "Open your default terminal in the current folder") do |_opts, _args, _cmd|
    log Paint["Launching shell ...", :yellow]
    log Paint["When it exits you will be back at this prompt.", :yellow]
    system ENV["SHELL"]
  end
end
upgrade_cmd() click to toggle source
# File lib/mux_tf/cli/current.rb, line 240
def upgrade_cmd
  define_cmd("upgrade", summary: "Upgrade modules/plguins") do |_opts, _args, _cmd|
    status, meta = run_upgrade
    if status != :ok
      log meta.inspect unless meta.empty?
      log "Upgrade Failed!"
    end
  end
end
validate() click to toggle source
# File lib/mux_tf/cli/current.rb, line 101
def validate
  log "Validating module ...", depth: 1
  tf_validate.parsed_output
end
version_check() click to toggle source
# File lib/mux_tf/cli/current.rb, line 64
def version_check
  if VersionCheck.has_updates?
    log Paint["=" * 80, :yellow]
    log "New version of #{Paint["mux_tf", :cyan]} is available!"
    log "You are currently on version: #{Paint[VersionCheck.current_gem_version, :yellow]}"
    log "Latest version found is: #{Paint[VersionCheck.latest_gem_version, :green]}"
    log "Run `#{Paint["gem install mux_tf", :green]}` to update!"
    log Paint["=" * 80, :yellow]
  end
end