module Specinfra::Backend::DockerLxc::ShellHelpers

Helpers to work with the shell and with `sudo`.

Uses the following `Specinfra` configuration options:

Based on the official `Specinfra::Backend::Ssh` code.

@example

class MyBackend < Specinfra::Backend::Base
  include Specinfra::Backend::DockerLxc::ShellHelpers

  def my_backend_run!
    stdout, stderr, status = shell_command!('uname -a')
    CommandResult.new(
      stdout: stdout, stderr: stderr, exit_status: status
    )
  end
end

Protected Instance Methods

default_sudo_args() click to toggle source

Returns the Sudo program arguments to use by default.

@return [String] the arguments properly escaped. @example

default_sudo_args #=> ""
default_sudo_args #=> "-S -p Password:\\ "
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 86
def default_sudo_args
  args = []
  args += ['-S', '-p', sudo_prompt] if sudo_password?
  args.shelljoin
end
escape_command(cmd) click to toggle source

Escapes a shell command.

It only escapes it when passed as array.

@param cmd [Array<String>, String] the command. @return [String] the command escaped. @example

escape_command(['sudo', '-p', 'Password: ')
  #=> "sudo -p Password:\\ "
escape_command('uname -a') #=> "uname -a"
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 158
def escape_command(cmd)
  return cmd if cmd.is_a?(String)
  cmd.shelljoin
end
generate_escaped_command(cmd) click to toggle source

Generates the command to run including the Sudo prefix if configured.

The command needs to be escaped only if passed as string.

@param cmd [Array<String>, String] the command. @return [String] the command escaped. @example

generate_escaped_command('uname -a') #=> "sudo uname -a"
set :sudo_password, 'y0mVT1CYM0uRiHxjLfNV'
generate_escaped_command('uname -a')
  #=> "sudo -p Password:\\  uname -a"
set :disable_sudo, true
generate_escaped_command('uname -a') #=> "uname -a"
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 176
def generate_escaped_command(cmd)
  if sudo?
    sudo_command(escape_command(cmd))
  else
    escape_command(cmd)
  end
end
shell_command!(cmd, opts = {}) click to toggle source

Runs a command, including Sudo if required.

If the command is passed as string, must be properly escaped.

@param cmd [Array<String>, String] the command. @return [Array<String, Fixnum>] the array contents: stdout, stderr

and the *exit status*.

@example

shell_command!('id')
  #=> ["", "uid=0(root) gid=0(root) groups=0(root)\n", 0]

@api public

# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 211
def shell_command!(cmd, opts = {})
  cmd_escaped = generate_escaped_command(cmd)
  Open3.popen3(cmd_escaped, opts) do |stdin, stdout, stderr, wait_thr|
    read = write_sudo_password(stdin, stderr)
    stdin.close
    [stdout.read, read + stderr.read, wait_thr.value.exitstatus]
  end
end
sudo?() click to toggle source

Checks if we need to use Sudo.

@return [TrueClass, FalseClass] whether we need to use Sudo. @example

sudo? #=> true
set :disable_sudo, true
sudo? #=> false
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 143
def sudo?
  disable_sudo = Specinfra.configuration.disable_sudo
  Etc.getlogin != 'root' && !disable_sudo
end
sudo_args() click to toggle source

Returns the Sudo program arguments.

Includes both the default and the arguments configured using `set`.

@return [String] the arguments properly escaped. @example

set :sudo_options, '-a -b -c'
sudo_args #=> "-S -p Password:\\  -a -b -c"
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 100
def sudo_args
  sudo_options = Specinfra.configuration.sudo_options
  if sudo_options
    sudo_options = sudo_options.shelljoin if sudo_options.is_a?(Array)
    "#{default_sudo_args} #{sudo_options}"
  else
    default_sudo_args
  end
end
sudo_bin() click to toggle source

Gets the Sudo binary path.

@return [String] the Sudo binary. @example

sudo_bin #=> "sudo"
set :sudo_path, '/opt/sudo/bin'
sudo_bin #=> "/opt/sudo/bin/sudo"
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 117
def sudo_bin
  sudo_path = Specinfra.configuration.sudo_path
  sudo_bin = sudo_path ? "#{sudo_path}/sudo" : 'sudo'
  sudo_bin.shellescape
end
sudo_command(cmd_str) click to toggle source

Adds Sudo to a command.

@param cmd_str [String] the command to run. Must be escaped. @return [String] the command escaped and including `sudo`. @example

set :sudo_password, 'y0mVT1CYM0uRiHxjLfNV'
sudo_command('uname -a') #=> "sudo -S -p Password:\\  -- uname -a"
set :disable_sudo, true
sudo_command('uname -a') #=> "uname -a"
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 132
def sudo_command(cmd_str)
  "#{sudo_bin} #{sudo_args} -- #{cmd_str}"
end
sudo_password() click to toggle source

Reads the `:sudo_password` configuration option.

@return [String] the password. @example

set :sudo_password, 'y0mVT1CYM0uRiHxjLfNV'
sudo_password #=> "y0mVT1CYM0uRiHxjLfNV"
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 67
def sudo_password
  Specinfra.configuration.sudo_password
end
sudo_password?() click to toggle source

Whether the `:sudo_password` is configured.

@return [TrueClass, FalseClass] true if password is configured. @example

sudo_password? #=> false
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 76
def sudo_password?
  sudo_password ? true : false
end
sudo_prompt() click to toggle source

Returns the prompt used by Sudo to ask for the password.

@return [String] the prompt. @example

sudo_prompt #=> "Password: "
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 57
def sudo_prompt
  'Password: '
end
write_sudo_password(stdin, stderr) click to toggle source

Writes the password to the stdin when asked on stderr.

@param stdin [IO] stdin file descriptor. @param stderr [IO] stderr file descriptor. @return [String] the string read from stderr without including the

password prompt.

@example

write_sudo_password(stdin, stderr) #=> ""
# File lib/specinfra/backend/docker_lxc/shell_helpers.rb, line 192
def write_sudo_password(stdin, stderr)
  return '' unless sudo_password?
  read = stderr.gets(sudo_prompt.length)
  return read.to_s unless read == sudo_prompt
  stdin.puts "#{sudo_password}\n"
  ''
end