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 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
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --no-private --protected lib/**/*.rb -m markdown - README.md
2
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
@@ -0,0 +1,4 @@
1
+ module SshExec
2
+ VERSION = IO.read(File.expand_path("../../../VERSION", __FILE__)).strip
3
+ end
4
+
@@ -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: []