ssh-exec 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/.yardopts +2 -0
- data/Gemfile +3 -0
- data/README.md +28 -0
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/lib/ssh-exec.rb +103 -0
- data/lib/ssh-exec/version.rb +4 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/ssh-exec_spec.rb +48 -0
- data/ssh-exec.gemspec +26 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f844d74dbe8f77a4c90f389a4f00861fc3f24cf5
|
4
|
+
data.tar.gz: 9abbaf177bee624d48c84145d37b4da7c1a971e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c97bc264e675ab1dd2c8b9d74afbb7514055c23b39521a0f0ec37d8c168e2735fd64394fe8cedb01b957256f5948cae5da0c69481f9ed0f056008fdeb4f1bc9c
|
7
|
+
data.tar.gz: a3102b7edea0db1f107de5317a9d0bad60863a0cd7b22da5efae5cf404e64291ff2292ded919ac8fd41f632b503caaad9a8ebd340cb610bdaa74ef906a66a392
|
data/.gitignore
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Documentation cache and generated files:
|
13
|
+
/.yardoc/
|
14
|
+
/_yardoc/
|
15
|
+
/doc/
|
16
|
+
/rdoc/
|
17
|
+
|
18
|
+
## Environment normalisation:
|
19
|
+
/.bundle/
|
20
|
+
/lib/bundler/man/
|
21
|
+
|
22
|
+
Gemfile.lock
|
23
|
+
.ruby-version
|
24
|
+
.ruby-gemset
|
25
|
+
|
26
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
27
|
+
.rvmrc
|
28
|
+
|
data/.rspec
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
## ssh-exec
|
2
|
+
|
3
|
+
`ssh-exec` is a wrapper around Net::SSH based on a
|
4
|
+
[StackOverflow answer](http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library/3386375#3386375),
|
5
|
+
allowing to easily capture standard output, standard error, and the exit code
|
6
|
+
of a command executed over [Net::SSH](https://github.com/net-ssh/net-ssh).
|
7
|
+
|
8
|
+
### Examples
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
require 'net/ssh'
|
12
|
+
require 'ssh-exec'
|
13
|
+
|
14
|
+
Net::SSH.start('somehost', 'someuser') do |ssh|
|
15
|
+
result = SshExec.ssh_exec!(ssh, 'echo I am remote host')
|
16
|
+
puts result.stdout # "I am remote host"
|
17
|
+
puts result.stderr # ""
|
18
|
+
puts result.exit_status # 0
|
19
|
+
|
20
|
+
result = SshExec.ssh_exec!(ssh, 'false')
|
21
|
+
puts result.exit_status # 1
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
### License
|
26
|
+
|
27
|
+
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
|
28
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
Bundler::GemHelper.install_tasks
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
|
12
|
+
task :build do
|
13
|
+
system "gem build #{GEM_NAME}.gemspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
task :release => :build do
|
17
|
+
system "gem push #{GEM_NAME}-#{SshExec::VERSION}"
|
18
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/ssh-exec.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# @markup markdown
|
2
|
+
|
3
|
+
require 'net/ssh'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
module SshExec
|
8
|
+
|
9
|
+
@@log = Logger.new(STDOUT)
|
10
|
+
@@log.level = Logger::INFO
|
11
|
+
|
12
|
+
|
13
|
+
class ExecutionError < StandardError
|
14
|
+
attr_reader :object
|
15
|
+
|
16
|
+
def initialize(object)
|
17
|
+
@object = object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Execute the given command using the given ssh object and capture its standard output, standard
|
22
|
+
# error, and exit status. The implementation is based on this
|
23
|
+
# {http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library/3386375#3386375
|
24
|
+
# StackOveflow answer}.
|
25
|
+
# @param ssh the {Net::SSH} shell to run the command in
|
26
|
+
# @param options [Hash] optional settings:
|
27
|
+
# `echo_stdout` - whether to echo standard output from the subcommand,
|
28
|
+
# `echo_stderr` - whether to echo standard error from the subcommand
|
29
|
+
# @return [OpenStruct] a struct containing `stdout`, `stderr`, `exit_status`, and `exit_signal`
|
30
|
+
def self.ssh_exec!(ssh, command, options = {})
|
31
|
+
options = options.clone
|
32
|
+
echo_stdout = options[:echo_stdout]
|
33
|
+
echo_stderr = options[:echo_stderr]
|
34
|
+
raise "Invalid options: #{options}" unless options.empty?
|
35
|
+
|
36
|
+
stdout_data = ""
|
37
|
+
stderr_data = ""
|
38
|
+
exit_code = nil
|
39
|
+
exit_signal = nil
|
40
|
+
ssh.open_channel do |channel|
|
41
|
+
channel.exec(command) do |ch, success|
|
42
|
+
unless success
|
43
|
+
raise "FAILED: couldn't execute command #{command}"
|
44
|
+
end
|
45
|
+
channel.on_data do |ch, data|
|
46
|
+
stdout_data += data
|
47
|
+
$stdout.write(data) if echo_stdout
|
48
|
+
end
|
49
|
+
|
50
|
+
channel.on_extended_data do |ch, type, data|
|
51
|
+
stderr_data += data
|
52
|
+
$stderr.write(data) if echo_stderr
|
53
|
+
end
|
54
|
+
|
55
|
+
channel.on_request("exit-status") do |ch, data|
|
56
|
+
exit_code = data.read_long
|
57
|
+
end
|
58
|
+
|
59
|
+
channel.on_request("exit-signal") do |ch, data|
|
60
|
+
exit_signal = data.read_long
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
ssh.loop
|
65
|
+
OpenStruct.new(
|
66
|
+
:stdout => stdout_data,
|
67
|
+
:stderr => stderr_data,
|
68
|
+
:exit_status => exit_code,
|
69
|
+
:exit_signal => exit_signal
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Runs the given command in the given SSH shell, and raises {ExecutionError}
|
74
|
+
# in case of failure (nonzero exit status). Logs the command in all cases.
|
75
|
+
# Logs the command, exit status, standard output and standard error in case of failure.
|
76
|
+
# @param ssh the {Net::SSH} shell to run the command in
|
77
|
+
# @param options [Hash] optional settings:
|
78
|
+
# `echo_stdout` - whether to echo stdout from the subcommand,
|
79
|
+
# `echo_stderr` - whether to echo stderr from the subcommand,
|
80
|
+
# `quiet` - to suppress logging the command and the error in case of non-zero exit status
|
81
|
+
# @return [OpenStruct] a struct containing `stdout`, `stderr`, `exit_status`, and `exit_signal`
|
82
|
+
def self.ensure_exec(ssh, command, options = {})
|
83
|
+
result = ssh_exec!(ssh, command, options)
|
84
|
+
|
85
|
+
options = options.clone
|
86
|
+
quiet = options.delete(:quiet)
|
87
|
+
|
88
|
+
@@log.info("Running on #{ssh.host}: #{command}") unless quiet
|
89
|
+
|
90
|
+
if result.exit_status != 0
|
91
|
+
@@log.error(
|
92
|
+
("Failed running command #{command}: exit status #{result.exit_status}. " +
|
93
|
+
(result.stdout.empty? ? "" : "Standard output:\n#{result.stdout}\n") +
|
94
|
+
(result.stderr.empty? ? "" : "Standard error:\n#{result.stderr}")).strip
|
95
|
+
) unless quiet
|
96
|
+
raise ExecutionError.new(
|
97
|
+
"Failed running command #{command}: exit status #{result.exit_status}"
|
98
|
+
)
|
99
|
+
end
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'net/ssh'
|
9
|
+
require 'etc'
|
10
|
+
|
11
|
+
require 'ssh-exec'
|
12
|
+
|
13
|
+
module SshHelpers
|
14
|
+
|
15
|
+
def ssh_localhost(&block)
|
16
|
+
Net::SSH.start('localhost', Etc.getlogin) do |ssh|
|
17
|
+
yield ssh
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
25
|
+
config.run_all_when_everything_filtered = true
|
26
|
+
config.filter_run :focus
|
27
|
+
|
28
|
+
# Run specs in random order to surface order dependencies. If you find an
|
29
|
+
# order dependency and want to debug it, you can fix the order by providing
|
30
|
+
# the seed, which is printed after each run.
|
31
|
+
# --seed 1234
|
32
|
+
config.order = 'random'
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'SshExec' do
|
4
|
+
include SshHelpers
|
5
|
+
|
6
|
+
describe 'SshExec.ssh_exec!' do
|
7
|
+
|
8
|
+
it 'capture_streams' do
|
9
|
+
ssh_localhost do |ssh|
|
10
|
+
result = SshExec.ssh_exec!(ssh,
|
11
|
+
'echo Hello Stdout; echo Hello Stderr >&2'
|
12
|
+
)
|
13
|
+
expect(result.stdout).to eq("Hello Stdout\n")
|
14
|
+
expect(result.stderr).to eq("Hello Stderr\n")
|
15
|
+
expect(result.exit_status).to eq(0)
|
16
|
+
expect(result.exit_signal).to be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'exit_status' do
|
21
|
+
ssh_localhost do |ssh|
|
22
|
+
[0, 1, 127, 128, 255].each do |status|
|
23
|
+
expect(SshExec.ssh_exec!(ssh, "exit #{status}").exit_status).to eq(status)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'SshExec.ensure_exec' do
|
31
|
+
it 'not_raise_on_success' do
|
32
|
+
ssh_localhost do |ssh|
|
33
|
+
result = SshExec.ensure_exec(ssh, 'echo Hello Stdout; echo Hello Stderr >&2')
|
34
|
+
expect(result.stdout).to eq("Hello Stdout\n")
|
35
|
+
expect(result.stderr).to eq("Hello Stderr\n")
|
36
|
+
expect(result.exit_status).to eq(0)
|
37
|
+
expect(result.exit_signal).to be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'raise_on_failure' do
|
42
|
+
ssh_localhost do |ssh|
|
43
|
+
expect { SshExec.ensure_exec(ssh, 'exit 1') }.to raise_error(SshExec::ExecutionError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/ssh-exec.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
GEM_NAME = 'ssh-exec'
|
2
|
+
|
3
|
+
require File.expand_path("../lib/#{GEM_NAME}/version", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ['Mikhail Bautin']
|
7
|
+
gem.email = ['mbautin@gmail.com']
|
8
|
+
gem.summary = 'A library allowing to execute commands over SSH using Net::SSH'
|
9
|
+
gem.description = gem.summary
|
10
|
+
gem.homepage = "http://github.com/mbautin/#{GEM_NAME}"
|
11
|
+
gem.licenses = ['Apache 2.0']
|
12
|
+
|
13
|
+
gem.executables = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f.strip) }
|
14
|
+
gem.files = `git ls-files`.split("\n").map(&:strip)
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split('\n').map(&:strip)
|
16
|
+
gem.name = GEM_NAME
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
gem.version = SshExec::VERSION
|
19
|
+
|
20
|
+
gem.add_dependency 'net-ssh', '~> 2.0'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rake', '~> 10.1'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
25
|
+
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ssh-exec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikhail Bautin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: net-ssh
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.14'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.14'
|
55
|
+
description: A library allowing to execute commands over SSH using Net::SSH
|
56
|
+
email:
|
57
|
+
- mbautin@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".yardopts"
|
65
|
+
- Gemfile
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- VERSION
|
69
|
+
- lib/ssh-exec.rb
|
70
|
+
- lib/ssh-exec/version.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
- spec/ssh-exec_spec.rb
|
73
|
+
- ssh-exec.gemspec
|
74
|
+
homepage: http://github.com/mbautin/ssh-exec
|
75
|
+
licenses:
|
76
|
+
- Apache 2.0
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.2.1
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: A library allowing to execute commands over SSH using Net::SSH
|
98
|
+
test_files: []
|