class Loom::Shell::Core
Attributes
dry_run[R]
mod_loader[R]
session[R]
shell_api[R]
Public Class Methods
new(mod_loader, sshkit_backend, dry_run=false)
click to toggle source
# File lib/loom/shell/core.rb, line 9 def initialize(mod_loader, sshkit_backend, dry_run=false) @dry_run = dry_run @mod_loader = mod_loader @sshkit_backend = sshkit_backend @session = Session.new @shell_api = Api.new self @cmd_wrappers = [] @sudo_users = [] # TODO: @sudo_dirs is a smelly workaround for not having a better # understanding of sudo security policies and inheriting environments. @sudo_dirs = [] end
Public Instance Methods
capture(*cmd_parts)
click to toggle source
# File lib/loom/shell/core.rb, line 149 def capture(*cmd_parts) if @dry_run # TODO: I'm not sure what to do about this. Loom.log.warn "`capture` during dry run won't do what you want" end execute *cmd_parts @session.last.stdout.strip end
cd(path, &block)
click to toggle source
Loom.log.debug3(self) { "sudo legacy... the other way" } sudo_stack_and_wrap(user, *sudo_cmd, &block) else Loom.log.debug3(self) { "sudo legacy" } sudo_stack_and_wrap(user, *sudo_cmd) end
end
# File lib/loom/shell/core.rb, line 135 def cd(path, &block) Loom.log.debug1(self) { "cd => #{path} #{block}" } # TODO: this might creates problems with relative paths, e.g. # loom.cd foo => cd ./foo # loom.sudo user => cd ./foo; sudo user @sudo_dirs.push path begin @sshkit_backend.within path, &block ensure @sudo_dirs.pop end end
execute(*cmd_parts, is_test: false, **cmd_opts)
click to toggle source
# File lib/loom/shell/core.rb, line 167 def execute(*cmd_parts, is_test: false, **cmd_opts) cmd_parts.compact! raise "empty command passed to execute" if cmd_parts.empty? sshkit_result = if @dry_run wrap(:printf, first: true) do r = execute_internal(*cmd_parts, **cmd_opts) Loom.log.info { "\t%s" % prompt_fmt(cmd_result.full_stdout.strip) } r end else execute_internal(*cmd_parts, **cmd_opts) end result = CmdResult.create_from_sshkit_command(sshkit_result, is_test, self) @session << result Loom.log.debug result.stdout unless result.stdout.empty? Loom.log.debug result.stderr unless result.stderr.empty? result end
Also aliased as: exec
is_sudo?()
click to toggle source
# File lib/loom/shell/core.rb, line 27 def is_sudo? !@sudo_users.empty? end
local()
click to toggle source
# File lib/loom/shell/core.rb, line 31 def local @local ||= LocalShell.new @mod_loader, @session, @dry_run end
pipe(*cmds)
click to toggle source
# File lib/loom/shell/core.rb, line 158 def pipe(*cmds) cmd = CmdWrapper.pipe *cmds.map { |*cmd| CmdWrapper.new *cmd } execute cmd end
sudo(user=nil, *sudo_cmd) { || ... }
click to toggle source
# File lib/loom/shell/core.rb, line 82 def sudo(user=nil, *sudo_cmd, &block) user ||= :root Loom.log.debug1(self) { "sudo => #{user} #{sudo_cmd} #{block}" } is_new_sudoer = @sudo_users.last.to_sym != user.to_sym rescue true @sudo_dirs.push(capture :pwd) @sudo_users.push << user if is_new_sudoer sudo_wrapper = [:sudo, "-u", user, "--", "/bin/sh", "-c"] sudo_cmd.compact! begin wrap *sudo_wrapper, :should_quote => true do execute *sudo_cmd unless sudo_cmd.empty? yield if block_given? end ensure @sudo_users.pop if is_new_sudoer @sudo_dirs.pop end end
test(*cmd, check: :exit_status, **cmd_opts)
click to toggle source
# File lib/loom/shell/core.rb, line 35 def test(*cmd, check: :exit_status, **cmd_opts) # TODO: is_test smells like a hack. I can't rely on Command#is_success? # here (returned from execute) because I'm overriding it with :is_test => # true. Fix Command#is_success? to not be a lie.. that is a lazy hack for # result reporting (I think the fix & feature) is to define Command # objects and declare style of reporting & error code handling it # has. Commands can be defined to ignore errors and just return their # results. # @see the TODO at Loom::Runner+execute_pattern+ execute *cmd, :is_test => true, **cmd_opts case check when :exit_status @session.last.exit_status == 0 when :stderr @session.last.stderr.empty? else raise "unknown test check => #{check}" end end
upload(local_path, remote_path)
click to toggle source
# File lib/loom/shell/core.rb, line 163 def upload(local_path, remote_path) @sshkit_backend.upload! local_path, remote_path end
verify(*check)
click to toggle source
# File lib/loom/shell/core.rb, line 56 def verify(*check) raise VerifyError, check unless test *check end
verify_which(command)
click to toggle source
# File lib/loom/shell/core.rb, line 60 def verify_which(command) verify :which, command end
wrap(*wrapper, first: false, should_quote: true) { || ... }
click to toggle source
# File lib/loom/shell/core.rb, line 64 def wrap(*wrapper, first: false, should_quote: true, &block) raise "missing block for +wrap+" unless block_given? cmd_wrapper = CmdWrapper.new(*wrapper, should_quote: should_quote) if first @cmd_wrappers.unshift(cmd_wrapper) else @cmd_wrappers.push(cmd_wrapper) end begin yield ensure first ? @cmd_wrappers.shift : @cmd_wrappers.pop end end
Protected Instance Methods
prompt_label()
click to toggle source
# File lib/loom/shell/core.rb, line 191 def prompt_label # TODO: get the real hostname. "remote" end
Private Instance Methods
create_command(*cmd_parts)
click to toggle source
Here be dragons. @return [String|Loom::Shell::CmdWrapper]
# File lib/loom/shell/core.rb, line 229 def create_command(*cmd_parts) cmd_wrapper = if cmd_parts.is_a? CmdWrapper Loom.log.debug3(self) { "existing cmd from args => #{cmd_parts}" } cmd_parts elsif cmd_parts[0].is_a? CmdWrapper Loom.log.debug3(self) { "existing cmd from args[0] => #{cmd_parts[0]}" } cmd_parts[0] else Loom.log.debug3(self) { "new cmd from args => #{cmd_parts}" } CmdWrapper.new *cmd_parts end # Useful for sudo, dry runs, timing a set of commands, or # timeout... anytime you want to prefix a group of commands. Reverses the # array to wrap from inner most call to `#{wrap}` to outer most. cmd = @cmd_wrappers.reverse.reduce(cmd_wrapper) do |cmd_or_wrapper, wrapper| Loom.log.debug3(self) { "wrapping cmds => #{wrapper} => #{cmd_or_wrapper}"} wrapper.wrap cmd_or_wrapper end unless @sudo_dirs.empty? || @dry_run cmd = "cd #{@sudo_dirs.last}; " << cmd.to_s end cmd end
execute_internal(*cmd_parts, pipe_to: [])
click to toggle source
# File lib/loom/shell/core.rb, line 202 def execute_internal(*cmd_parts, pipe_to: []) primary_cmd = create_command *cmd_parts # TODO: where is piped_cmds used? piped_cmds = pipe_to.map { |cmd_parts| CmdWrapper.new *cmd_parts } cmd = CmdPipeline.new([primary_cmd].concat(piped_cmds)).to_s # Tests if the command looks like "echo\ hi", the trailing slash after # echo indicates that just 1 big string was passed in and we can't really # isolate the execuatable part of the command. This might be fine, but # it's better to be strict now and relax this later if it's OK. if cmd.match /^[\w\-\[]+\\/i raise "use array parts for command escaping => #{cmd}" end Loom.log.debug1(self) { "executing => #{cmd}" } # This is a big hack to get access to the SSHKit command # object and avoid the automatic errors thrown on non-zero # error codes @sshkit_backend.send( :create_command_and_execute, cmd, :raise_on_non_zero_exit => false) end
prompt_fmt(*cmd_parts)
click to toggle source
# File lib/loom/shell/core.rb, line 197 def prompt_fmt(*cmd_parts) output = Shellwords.join(cmd_parts).gsub /\\/, '' "[%s]:$ %s" % [prompt_label, output] end