ssh-exec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []