Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify backend hierarchy #237

Merged
merged 18 commits into from
Apr 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ appear at the top.

* Add your entries below here, remember to credit yourself however you want
to be credited!
* Simplified backend hierarchy. @robd
* Moved duplicate implementations of `make`, `rake`, `test`, `capture`, `background` on to `Abstract` backend.
* Backend implementations now only need to implement `execute_command`, `upload!` and `download!`
* Removed `Printer` from backend hierarchy for `Local` and `Netssh` backends (they now just extend `Abstract`)
* Removed unused `Net::SSH:LogLevelShim`

## 1.7.1

Expand Down
44 changes: 33 additions & 11 deletions lib/sshkit/backends/abstract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Abstract
attr_reader :host

def run
# Nothing to do
instance_exec(@host, &@block)
end

def initialize(host, &block)
Expand Down Expand Up @@ -47,23 +47,32 @@ def trace(messages)
end

def make(commands=[])
raise MethodUnavailableError
execute :make, commands
end

def rake(commands=[])
raise MethodUnavailableError
execute :rake, commands
end

def test(command, args=[])
raise MethodUnavailableError
def test(*args)
options = args.extract_options!.merge(raise_on_non_zero_exit: false, verbosity: Logger::DEBUG)
create_command_and_execute(args, options).success?
end

def execute(command, args=[])
raise MethodUnavailableError
def capture(*args)
options = { verbosity: Logger::DEBUG }.merge(args.extract_options!)
create_command_and_execute(args, options).full_stdout
end

def capture(command, args=[])
raise MethodUnavailableError
def background(*args)
warn "[Deprecated] 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)
Expand Down Expand Up @@ -118,10 +127,23 @@ def configure
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 command(*args)
options = args.extract_options!
def output
SSHKit.config.output
end

def create_command_and_execute(args, options)
command(args, options).tap { |cmd| execute_command(cmd) }
end

def command(args, options)
SSHKit::Command.new(*[*args, options.merge({in: @pwd.nil? ? nil : File.join(@pwd), env: @env, host: @host, user: @user, group: @group})])
end

Expand Down
69 changes: 23 additions & 46 deletions lib/sshkit/backends/local.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,13 @@ module SSHKit

module Backend

class Local < Printer
class Local < Abstract

def initialize(_ = nil, &block)
@host = Host.new(:local) # just for logging
@block = block
end

def run
instance_exec(@host, &@block)
end

def test(*args)
options = args.extract_options!.merge(
raise_on_non_zero_exit: false,
verbosity: Logger::DEBUG
)
_execute(*[*args, options]).success?
end

def execute(*args)
_execute(*args).success?
end

def capture(*args)
options = { verbosity: Logger::DEBUG }.merge(args.extract_options!)
_execute(*[*args, options]).full_stdout
end

def upload!(local, remote, options = {})
if local.is_a?(String)
FileUtils.cp(local, remote)
Expand All @@ -54,40 +33,38 @@ def download!(remote, local=nil, options = {})

private

def _execute(*args)
command(*args).tap do |cmd|
output << cmd
def execute_command(cmd)
output << cmd

cmd.started = Time.now
cmd.started = Time.now

Open3.popen3(cmd.to_command) do |stdin, stdout, stderr, wait_thr|
stdout_thread = Thread.new do
while line = stdout.gets do
cmd.stdout = line
cmd.full_stdout += line
Open3.popen3(cmd.to_command) do |stdin, stdout, stderr, wait_thr|
stdout_thread = Thread.new do
while line = stdout.gets do
cmd.stdout = line
cmd.full_stdout += line

output << cmd
end
output << cmd
end
end

stderr_thread = Thread.new do
while line = stderr.gets do
cmd.stderr = line
cmd.full_stderr += line
stderr_thread = Thread.new do
while line = stderr.gets do
cmd.stderr = line
cmd.full_stderr += line

output << cmd
end
output << cmd
end
end

stdout_thread.join
stderr_thread.join
stdout_thread.join
stderr_thread.join

cmd.exit_status = wait_thr.value.to_i
cmd.stdout = ''
cmd.stderr = ''
cmd.exit_status = wait_thr.value.to_i
cmd.stdout = ''
cmd.stderr = ''

output << cmd
end
output << cmd
end
end

Expand Down
143 changes: 49 additions & 94 deletions lib/sshkit/backends/netssh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,9 @@ def default_files

module SSHKit

class Logger

class Net::SSH::LogLevelShim
attr_reader :output
def initialize(output)
@output = output
end
def debug(args)
output << LogMessage.new(Logger::TRACE, args)
end
def error(args)
output << LogMessage.new(Logger::ERROR, args)
end
def lwarn(args)
output << LogMessage.new(Logger::WARN, args)
end
end

end

module Backend

class Netssh < Printer
class Netssh < Abstract

class Configuration
attr_accessor :connection_timeout, :pty
Expand All @@ -48,33 +28,10 @@ def ssh_options
end
end

include SSHKit::CommandHelper

def run
instance_exec(host, &@block)
end

def test(*args)
options = args.extract_options!.merge(
raise_on_non_zero_exit: false,
verbosity: Logger::DEBUG
)
_execute(*[*args, options]).success?
end

def execute(*args)
_execute(*args).success?
end

def background(*args)
warn "[Deprecated] The background method is deprecated. Blame badly behaved pseudo-daemons!"
options = args.extract_options!.merge(run_in_background: true)
_execute(*[*args, options]).success?
end

def capture(*args)
options = { verbosity: Logger::DEBUG }.merge(args.extract_options!)
_execute(*[*args, options]).full_stdout.strip
# The behaviour to strip the full stdout was removed from the local backend in commit 7d15a9a,
# but was left in for the net ssh backend.
super(*args).strip
end

def upload!(local, remote, options = {})
Expand Down Expand Up @@ -129,56 +86,54 @@ def transfer_summarizer(action)
end
end

def _execute(*args)
command(*args).tap do |cmd|
output << cmd
cmd.started = true
exit_status = nil
with_ssh do |ssh|
ssh.open_channel do |chan|
chan.request_pty if Netssh.config.pty
chan.exec cmd.to_command do |ch, success|
chan.on_data do |ch, data|
cmd.stdout = data
cmd.full_stdout += data
output << cmd
end
chan.on_extended_data do |ch, type, data|
cmd.stderr = data
cmd.full_stderr += data
output << cmd
end
chan.on_request("exit-status") do |ch, data|
exit_status = data.read_long
end
#chan.on_request("exit-signal") do |ch, data|
# # TODO: This gets called if the program is killed by a signal
# # might also be a worthwhile thing to report
# exit_signal = data.read_string.to_i
# warn ">>> " + exit_signal.inspect
# output << cmd
#end
chan.on_open_failed do |ch|
# TODO: What do do here?
# I think we should raise something
end
chan.on_process do |ch|
# TODO: I don't know if this is useful
end
chan.on_eof do |ch|
# TODO: chan sends EOF before the exit status has been
# writtend
end
def execute_command(cmd)
output << cmd
cmd.started = true
exit_status = nil
with_ssh do |ssh|
ssh.open_channel do |chan|
chan.request_pty if Netssh.config.pty
chan.exec cmd.to_command do |ch, success|
chan.on_data do |ch, data|
cmd.stdout = data
cmd.full_stdout += data
output << cmd
end
chan.on_extended_data do |ch, type, data|
cmd.stderr = data
cmd.full_stderr += data
output << cmd
end
chan.on_request("exit-status") do |ch, data|
exit_status = data.read_long
end
#chan.on_request("exit-signal") do |ch, data|
# # TODO: This gets called if the program is killed by a signal
# # might also be a worthwhile thing to report
# exit_signal = data.read_string.to_i
# warn ">>> " + exit_signal.inspect
# output << cmd
#end
chan.on_open_failed do |ch|
# TODO: What do do here?
# I think we should raise something
end
chan.on_process do |ch|
# TODO: I don't know if this is useful
end
chan.on_eof do |ch|
# TODO: chan sends EOF before the exit status has been
# writtend
end
chan.wait
end
ssh.loop
end
# Set exit_status and log the result upon completion
if exit_status
cmd.exit_status = exit_status
output << cmd
chan.wait
end
ssh.loop
end
# Set exit_status and log the result upon completion
if exit_status
cmd.exit_status = exit_status
output << cmd
end
end

Expand Down
25 changes: 2 additions & 23 deletions lib/sshkit/backends/printer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,12 @@ module Backend

class Printer < Abstract

include SSHKit::CommandHelper

def run
instance_exec(host, &@block)
end

def execute(*args)
command(*args).tap do |cmd|
output << cmd
end
def execute_command(cmd)
output << cmd
end
alias :upload! :execute
alias :download! :execute
alias :test :execute

def capture(*args)
String.new.tap { execute(*args) }
end
alias :capture! :capture


private

def output
SSHKit.config.output
end

end
end
end
Loading