Class: Bolt::Transport::Sudoable::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/bolt/transport/sudoable/connection.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target) ⇒ Connection

Returns a new instance of Connection.



10
11
12
13
14
# File 'lib/bolt/transport/sudoable/connection.rb', line 10

def initialize(target)
  @target = target
  @run_as = nil
  @logger = Logging.logger[@target.safe_name]
end

Instance Attribute Details

#targetObject

Returns the value of attribute target.



9
10
11
# File 'lib/bolt/transport/sudoable/connection.rb', line 9

def target
  @target
end

Instance Method Details

#build_sudoable_command_str(command_str, sudo_str, sudo_id, options) ⇒ Object

A helper to build up a single string that contains all of the options for privilege escalation. A wrapper script is used to direct task input to stdin when a tty is allocated and thus we do not need to prepend_sudo_success when using the wrapper or when the task does not require stdin data.



86
87
88
89
90
91
92
# File 'lib/bolt/transport/sudoable/connection.rb', line 86

def build_sudoable_command_str(command_str, sudo_str, sudo_id, options)
  if options[:stdin] && !options[:wrapper]
    "#{sudo_str} #{prepend_sudo_success(sudo_id, command_str)}"
  else
    "#{sudo_str} #{command_str}"
  end
end

#execute(*_args) ⇒ Object

Raises:

  • (NotImplementedError)


69
70
71
72
# File 'lib/bolt/transport/sudoable/connection.rb', line 69

def execute(*_args)
  message = "#{self.class.name} must implement #{method} to execute commands"
  raise NotImplementedError, message
end

#inject_interpreter(interpreter, command) ⇒ Object

Returns string with the interpreter conditionally prepended



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/bolt/transport/sudoable/connection.rb', line 95

def inject_interpreter(interpreter, command)
  if interpreter
    if command.is_a?(Array)
      command.unshift(interpreter)
    else
      command = [interpreter, command]
    end
  end

  command.is_a?(String) ? command : Shellwords.shelljoin(command)
end

#make_executable(path) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/bolt/transport/sudoable/connection.rb', line 31

def make_executable(path)
  result = execute(['chmod', 'u+x', path])
  if result.exit_code != 0
    message = "Could not make file '#{path}' executable: #{result.stderr.string}"
    raise Bolt::Node::FileError.new(message, 'CHMOD_ERROR')
  end
end

#make_tempdirObject



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/bolt/transport/sudoable/connection.rb', line 39

def make_tempdir
  tmpdir = @target.options.fetch('tmpdir', '/tmp')
  tmppath = "#{tmpdir}/#{SecureRandom.uuid}"
  command = ['mkdir', '-m', 700, tmppath]

  result = execute(command)
  if result.exit_code != 0
    raise Bolt::Node::FileError.new("Could not make tempdir: #{result.stderr.string}", 'TEMPDIR_ERROR')
  end
  path = tmppath || result.stdout.string.chomp
  Sudoable::Tmpdir.new(self, path)
end

#prepend_sudo_success(sudo_id, command_str) ⇒ Object

In the case where a task is run with elevated privilege and needs stdin a random string is echoed to stderr indicating that the stdin is available for task input data because the sudo password has already either been provided on stdin or was not needed.



78
79
80
# File 'lib/bolt/transport/sudoable/connection.rb', line 78

def prepend_sudo_success(sudo_id, command_str)
  "sh -c 'echo #{sudo_id} 1>&2; #{command_str}'"
end

#run_asObject

This method allows the @run_as variable to be used as a per-operation override for the user to run as. When @run_as is unset, the user specified on the target will be used.



19
20
21
# File 'lib/bolt/transport/sudoable/connection.rb', line 19

def run_as
  @run_as || target.options['run-as']
end

#running_as(user) ⇒ Object

Run as the specified user for the duration of the block.



24
25
26
27
28
29
# File 'lib/bolt/transport/sudoable/connection.rb', line 24

def running_as(user)
  @run_as = user
  yield
ensure
  @run_as = nil
end

#with_tempdirObject

A helper to create and delete a tempdir on the remote system. Yields the directory name.



62
63
64
65
66
67
# File 'lib/bolt/transport/sudoable/connection.rb', line 62

def with_tempdir
  dir = make_tempdir
  yield dir
ensure
  dir&.delete
end

#write_executable(dir, file, filename = nil) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/bolt/transport/sudoable/connection.rb', line 52

def write_executable(dir, file, filename = nil)
  filename ||= File.basename(file)
  remote_path = File.join(dir.to_s, filename)
  copy_file(file, remote_path)
  make_executable(remote_path)
  remote_path
end