1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
require 'shellwords'
module SSHKit
module Backend
MethodUnavailableError = Class.new(SSHKit::StandardError)
# The Backend instance that is running in the current thread. If no Backend
# is running, returns `nil` instead.
#
# Example:
#
# on(:local) do
# self == SSHKit::Backend.current # => true
# end
#
def self.current
Thread.current["sshkit_backend"]
end
class Abstract
extend Forwardable
def_delegators :output, :log, :fatal, :error, :warn, :info, :debug
attr_reader :host
def run
Thread.current["sshkit_backend"] = self
instance_exec(@host, &@block)
ensure
Thread.current["sshkit_backend"] = nil
end
def initialize(host, &block)
raise "Must pass a Host object" unless host.is_a? Host
@host = host
@block = block
@pwd = nil
@env = nil
@user = nil
@group = nil
end
def redact(arg) # Used in execute_command to hide redact() args a user passes in
arg.to_s.extend(Redaction) # to_s due to our inability to extend Integer, etc
end
def make(commands=[])
execute :make, commands
end
def rake(commands=[])
execute :rake, commands
end
def test(*args)
options = { verbosity: Logger::DEBUG, raise_on_non_zero_exit: false }.merge(args.extract_options!)
create_command_and_execute(args, options).success?
end
def capture(*args)
options = { verbosity: Logger::DEBUG, strip: true }.merge(args.extract_options!)
result = create_command_and_execute(args, options).full_stdout
options[:strip] ? result.strip : result
end
def background(*args)
SSHKit.config.deprecation_logger.log(
'The background method is deprecated. Blame badly behaved pseudo-daemons!'
)
options = args.extract_options!.merge(run_in_background: true)
create_command_and_execute(args, options).success?
end
def execute(*args)
options = args.extract_options!
create_command_and_execute(args, options).success?
end
def within(directory, &_block)
(@pwd ||= []).push directory.to_s
escaped = Command.shellescape_except_tilde(pwd_path)
execute <<-EOTEST, verbosity: Logger::DEBUG
if test ! -d #{escaped}
then echo "Directory does not exist '#{escaped}'" 1>&2
false
fi
EOTEST
yield
ensure
@pwd.pop
end
def with(environment, &_block)
env_old = (@env ||= {})
@env = env_old.merge environment
yield
ensure
@env = env_old
end
def as(who, &_block)
if who.is_a? Hash
@user = who[:user] || who["user"]
@group = who[:group] || who["group"]
else
@user = who
@group = nil
end
execute <<-EOTEST, verbosity: Logger::DEBUG
if ! sudo -u #{@user.to_s.shellescape} whoami > /dev/null
then echo "You cannot switch to user '#{@user.to_s.shellescape}' using sudo, please check the sudoers file" 1>&2
false
fi
EOTEST
yield
ensure
remove_instance_variable(:@user)
remove_instance_variable(:@group)
end
class << self
def config
@config ||= OpenStruct.new
end
def configure
yield config
end
end
# Backends which extend the Abstract backend should implement the following methods:
def upload!(_local, _remote, _options = {}) raise MethodUnavailableError end
def download!(_remote, _local=nil, _options = {}) raise MethodUnavailableError end
def execute_command(_cmd) raise MethodUnavailableError end
private :execute_command # Can inline after Ruby 2.1
private
def output
SSHKit.config.output
end
def create_command_and_execute(args, options)
command(args, options).tap { |cmd| execute_command(cmd) }
end
def pwd_path
if @pwd.nil? || @pwd.empty?
nil
else
File.join(@pwd)
end
end
def command(args, options)
SSHKit::Command.new(*args, options.merge({in: pwd_path, env: @env, host: @host, user: @user, group: @group}))
end
end
end
end
|