class Terraspace::Shell

Constants

BLOCK_SIZE
BUFFER_TIMEOUT

Public Class Methods

new(mod, command, options={}) click to toggle source
# File lib/terraspace/shell.rb, line 7
def initialize(mod, command, options={})
  @mod, @command, @options = mod, command, options
end

Public Instance Methods

all_eof?(files) click to toggle source
# File lib/terraspace/shell.rb, line 80
def all_eof?(files)
  files.find { |f| !f.eof }.nil?
end
exit_status(status) click to toggle source
# File lib/terraspace/shell.rb, line 100
def exit_status(status)
  return if status == 0

  exit_on_fail = @options[:exit_on_fail].nil? ? true : @options[:exit_on_fail]
  if @error && @error.known?
    raise @error.instance
  elsif exit_on_fail
    logger.error "Error running command: #{@command}".color(:red)
    exit status
  end
end
handle_input(stdin, line) click to toggle source

Terraform doesnt seem to stream the line that prompts with “Enter a value:” when using Open3.popen3 Hack around it by mimicking the “Enter a value:” prompt

Note: system does stream the prompt but using Open3.popen3 so we can capture output to save to logs.

# File lib/terraspace/shell.rb, line 88
def handle_input(stdin, line)
  # stdout doesnt seem to flush and show "Enter a value: " look for earlier output
  patterns = [
    "Only 'yes' will be accepted", # prompt for apply. can happen on apply
    "\e[0m\e[1mvar.", # prompts for variable input. can happen on plan or apply. looking for bold marker also in case "var." shows up somewhere else
  ]
  if patterns.any? { |pattern| line.include?(pattern) }
    print "\n  Enter a value: ".bright
    stdin.write_nonblock($stdin.gets)
  end
end
handle_stderr(line) click to toggle source
# File lib/terraspace/shell.rb, line 68
def handle_stderr(line)
  @error ||= Error.new
  @error.lines << line # aggregate all error lines

  return if @error.known?
  # Sometimes may print a "\e[31m\n" which like during dependencies fetcher init
  # suppress it so dont get a bunch of annoying "newlines"
  return if line == "\e[31m\n" && @options[:suppress_error_color]

  logger.error(line)
end
handle_stdout(line) click to toggle source
# File lib/terraspace/shell.rb, line 112
def handle_stdout(line)
  prompted = line.include?('Enter a value')
  @prompt_shown ||= prompted
  return if @prompt_shown && prompted

  # Terraspace logger has special stdout method so original terraform output
  # can be piped to jq. IE:
  #   terraspace show demo --json | jq
  if logger.respond_to?(:stdout) && !@options[:log_to_stderr]
    logger.stdout(line)
  else
    logger.info(line)
  end
end
handle_streams(stdin, stdout, stderr) click to toggle source
# File lib/terraspace/shell.rb, line 40
def handle_streams(stdin, stdout, stderr)
  # note: t=0 and t=nil means no timeout. See: https://bit.ly/2PURlCX
  t = BUFFER_TIMEOUT.to_i unless BUFFER_TIMEOUT.nil?
  Timeout::timeout(t) do
    files = [stdout, stderr]
    until all_eof?(files) do
      ready = IO.select(files)
      next unless ready

      readable = ready[0]
      readable.each do |f|
        buffer = f.read_nonblock(BLOCK_SIZE, exception: false)
        next unless buffer

        lines = buffer.split("\n")
        lines.each do |line|
          if f.fileno == stdout.fileno
            handle_stdout(line)
            handle_input(stdin, line)
          else
            handle_stderr(line)
          end
        end
      end
    end
  end
end
popen3(env) click to toggle source
# File lib/terraspace/shell.rb, line 30
def popen3(env)
  Open3.popen3(env, @command, chdir: @mod.cache_dir) do |stdin, stdout, stderr, wait_thread|
    handle_streams(stdin, stdout, stderr)
    status = wait_thread.value.exitstatus
    exit_status(status)
  end
end
run() click to toggle source

requires @mod to be set quiet useful for RemoteState::Fetcher

# File lib/terraspace/shell.rb, line 13
def run
  msg = "=> #{@command}"
  @options[:quiet] ? logger.debug(msg) : logger.info(msg)
  return if ENV['TS_TEST']
  shell
end
shell() click to toggle source
# File lib/terraspace/shell.rb, line 20
def shell
  env = @options[:env] || {}
  env.stringify_keys!
  if @options[:shell] == "system" # terraspace console
    system(env, @command, chdir: @mod.cache_dir)
  else
    popen3(env)
  end
end