open4ssh 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: 78635fba401d382c7a6cf79897cb94e89ec54175
4
+ data.tar.gz: 3a33cebef1a16d8071d4dc2c7aefa849a6f60888
5
+ SHA512:
6
+ metadata.gz: 0f380979bfca37db680e5bf1e0f7ba1c4966279ee5e879caafbc73216d261a48ebab5905f8641ca1c8c9acf09029d5a42420a35f55b90ff975551482f18aa5ed
7
+ data.tar.gz: 0e11987f6bcdb82f4dd1c3586dade177189dfc8a6495fa89913afb8f72868ed62676c75d0f7129afb577b4b14c85b4238042da2e5d698b19d987c6e60e9f90cb
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.idea/
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in open4ssh.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Nane Kratzke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,230 @@
1
+ # Open4ssh
2
+
3
+ [Open4ssh](https://github.com/nkratzke/open4ssh) is a small convenience wrapper for [net-ssh](https://rubygems.org/gems/net-ssh).
4
+ Its intended and primary purpose is to provide pragmatic
5
+ execution of shell commands on a remote host via SSH.
6
+
7
+ It is mainly inspired by [Open3](http://ruby-doc.org/stdlib-2.3.1/libdoc/open3/rdoc/Open3.html) standard library
8
+ which provides access to exit codes,
9
+ standard output and standard error messages of executed commands on local host.
10
+ Open4ssh does the same but in a SSH remote context.
11
+ Astonishingly, there seems no pragmatic way to figure out exit codes or standard error messages of executed commands
12
+ with the net-ssh library.
13
+ Additionally, Open4ssh is able to execute a
14
+ sequence of commands and returns their exit codes, standard out and standard error messages in a command related list.
15
+
16
+ Open4ssh is most useful in remote automation scenarios which are triggered from Ruby environments.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'open4ssh'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install open4ssh
33
+
34
+ ## Usage
35
+
36
+ ### Remote execution of a single command via SSH
37
+
38
+ All parameters of the _exec_ command are explained [here](Open4ssh.html#exec-class_method).
39
+
40
+ However, the following snippets explain how to use Open4ssh.
41
+ To execute simply one single command on a remote host, we can do the following:
42
+
43
+ ```ruby
44
+ require 'open4ssh'
45
+
46
+ stdout = Open4ssh.exec(
47
+ host: 'remote.host.io',
48
+ user: 'nane',
49
+ pwd: 'secret',
50
+ cmd: 'ls -la'
51
+ )
52
+ puts stdout
53
+ ```
54
+
55
+ This will execute the bash command 'ls -la' on host _remote.host.io_ as user _nane_.
56
+
57
+ For a lot of cloud scenarios it is more appropriate to support a keybased login. This can be done like that
58
+ (simply use the key parameter instead of the pwd parameter):
59
+
60
+ ```ruby
61
+ require 'open4ssh'
62
+
63
+ stdout = Open4ssh.exec(
64
+ host: 'remote.host.io',
65
+ user: 'nane',
66
+ key: '/path/to/your/sshkey.pem',
67
+ cmd: 'ls -la'
68
+ )
69
+ puts stdout
70
+ ```
71
+
72
+ ### Remote execution of a sequence of commands via SSH
73
+
74
+ All parameters of the _exec4_ command are explained [here](Open4ssh.html#exec4-class_method).
75
+ The following snippets will explain how to use Open4ssh to execute a (sequence) of commands.
76
+
77
+ This snippet here will execute five shell commands sequentially
78
+
79
+ ```ruby
80
+ require 'open4ssh'
81
+ require 'pp'
82
+
83
+ returns = Open4ssh.exec4(
84
+ host: 'remote.host.io',
85
+ user: 'nane',
86
+ key: '/path/to/your/sshkey.pem',
87
+ cmd: [
88
+ "touch helloworld.txt",
89
+ "cat helloworld.txt",
90
+ "echo 'Hello World' >> helloworld.txt",
91
+ "cat helloworld.txt",
92
+ "rm helloworld.txt"
93
+ ]
94
+ )
95
+ pp(returns)
96
+ ```
97
+
98
+ and will generate this output.
99
+
100
+ [[0, "", "", "touch helloworld.txt"],
101
+ [0, "", "", "cat helloworld.txt"],
102
+ [0, "", "", "echo 'Hello World' >> helloworld.txt"],
103
+ [0, "Hello World\n", "", "cat helloworld.txt"],
104
+ [0, "", "", "rm helloworld.txt"]]
105
+
106
+ So, for each command a list of return values is returned.
107
+
108
+ 1. exit code of the executed command
109
+ 2. standard out message (might be empty)
110
+ 3. standard error message (might be empty)
111
+ 4. executed command (as passed by the _cmd_ parameter in _exec4_ call.
112
+
113
+ However, if we launch a sequence of commands exiting with exit codes not equal 0, this sequence is only executed as long as
114
+ each command could be successfully processed (exit code 0). So this snippet here
115
+
116
+ ```ruby
117
+ require 'open4ssh'
118
+ require 'pp'
119
+
120
+ returns = Open4ssh.exec4(
121
+ host: 'remote.host.io',
122
+ user: 'nane',
123
+ key: '/path/to/your/sshkey.pem',
124
+ cmd: [
125
+ "touch helloworld.txt",
126
+ "cat helloworld.txt",
127
+ "this will not work",
128
+ "cat helloworld.txt",
129
+ "rm helloworld.txt"
130
+ ]
131
+ )
132
+ pp(returns)
133
+ ```
134
+
135
+ would produce the following output
136
+
137
+ [[0, "", "", "touch helloworld.txt"],
138
+ [0, "", "", "cat helloworld.txt"],
139
+ [127, "", "bash: this: command not found\n", "this will not work"]]
140
+
141
+ and the last two commands would not been executed on the remote host, because the third command failed.
142
+
143
+ ### How to check whether a sequence of commands was successful?
144
+
145
+ Because Open4ssh only executes commands as long as they are returning a exit code of 0, we can check
146
+ pragmatically whether all commands of a sequence have been executed successfully:
147
+
148
+ ```ruby
149
+ exit_code, stderr, stdout, command = Open4ssh.exec4(
150
+ host: 'remote.host.io',
151
+ user: 'nane',
152
+ key: '/path/to/your/sshkey.pem',
153
+ cmd: [
154
+ "touch helloworld.txt",
155
+ "cat helloworld.txt",
156
+ "echo 'Hello World' >> helloworld.txt",
157
+ "cat helloworld.txt",
158
+ "rm helloworld.txt"
159
+ ]
160
+ ).last
161
+
162
+ if (exit_code == 0 && command == "rm helloworld.txt")
163
+ puts "Everything worked fine"
164
+ end
165
+ ```
166
+
167
+ ### What is this good for?
168
+
169
+ Just a small example. Assuming your remote host is a Ubuntu 14.04 system we could do something like that:
170
+
171
+ ```ruby
172
+ exit_code, stdout, stderr, command = Open4ssh.exec4(
173
+ host: 'remote.host.io',
174
+ user: 'nane',
175
+ key: '/path/to/your/sshkey.pem',
176
+ cmd: [
177
+ "curl -fsSL https://test.docker.com/ | sh",
178
+ "sudo docker swarm init"
179
+ ]
180
+ ).last
181
+
182
+ if (command == "sudo docker swarm init" && exit_code == 0)
183
+ puts "You started successfully a new Docker Swarm cluster."
184
+ end
185
+ ```
186
+
187
+ This would fire up an initial master for a [Docker Swarm cluster](https://docs.docker.com/engine/swarm/)
188
+ in a few lines of Ruby code. Be patient. This can take several minutes.
189
+ Of course, you can do any other tasks as well. This was only one example ;-)
190
+
191
+ ### Verbose mode
192
+
193
+ If you want to know what is happening there you can turn on the verbose mode (mostly useful for debugging).
194
+
195
+ ```ruby
196
+ exit_code, stdout, stderr, command = Open4ssh.exec4(
197
+ host: 'remote.host.io',
198
+ user: 'nane',
199
+ key: '/path/to/your/sshkey.pem',
200
+ cmd: [
201
+ "curl -fsSL https://test.docker.com/ | sh",
202
+ "sudo docker swarm init"
203
+ ],
204
+ verbose: true
205
+ ).last
206
+
207
+ if (command == "sudo docker swarm init" && exit_code == 0)
208
+ puts "You started successfully a new Docker Swarm cluster."
209
+ end
210
+ ```
211
+
212
+ This will perform the same install like above but will print all messages of the Docker install script on your console.
213
+
214
+ ## Development
215
+
216
+ To install this gem onto your local machine, run `bundle exec rake install`.
217
+ To release a new version, update the version number in `version.rb`,
218
+ and then run `bundle exec rake release`,
219
+ which will create a git tag for the version,
220
+ push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
221
+
222
+ ## Contributing
223
+
224
+ Bug reports and pull requests are welcome on Github at https://github.com/nkratzke/open4ssh.
225
+
226
+
227
+ ## License
228
+
229
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
230
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'yard'
3
+
4
+ YARD::Rake::YardocTask.new do |t|
5
+ t.files = ['lib/**/*.rb', '-', 'README.md', 'LICENSE.txt']
6
+ # t.options = ['--any', '--extra', '--opts']
7
+ t.stats_options = ['--list-undoc']
8
+ end
@@ -0,0 +1,3 @@
1
+ module Open4ssh
2
+ VERSION = "0.1.0"
3
+ end
data/lib/open4ssh.rb ADDED
@@ -0,0 +1,82 @@
1
+ require "open4ssh/version"
2
+ require "net/ssh"
3
+
4
+ module Open4ssh
5
+
6
+ # Executes a shell command on a remote host via SSH and returns the console output.
7
+ #
8
+ # @param host [String] DNS name or IP address of the remote host (required)
9
+ # @param port [Integer] Port (defaults to 22)
10
+ # @param user [String] User name (required)
11
+ # @param key [Path] Path to a key file (.pem) if user logs in via keyfile (not required if password is provided)
12
+ # @param pwd [String] Password of user (not required if key is provided)
13
+ # @param cmd [String] valid shell command string to be executed on host (required)
14
+ #
15
+ # @return [String] console output of executed command (output includes stdout and stderr)
16
+ #
17
+ def self.exec(host: '', user: '', port: 22, key: '', pwd: '', cmd: '')
18
+ stdout = ""
19
+ keys = [key]
20
+
21
+ Net::SSH.start(host, user, port: 22, password: pwd, keys: keys) do |ssh|
22
+ result = ssh.exec!(cmd)
23
+ stdout = result
24
+ end
25
+
26
+ return stdout
27
+ end
28
+
29
+ # Executes a list of shell commands on a remote host via SSH and returns their exit codes, stdouts and stderrs.
30
+ # The commands are executed sequentially until a command terminates with an exit code not equal 0 (no success).
31
+ #
32
+ # @param host [String] DNS name or IP address of the remote host (required)
33
+ # @param port [Integer] Port (defaults to 22)
34
+ # @param user [String] User name (required)
35
+ # @param key [Path] Path to a key file (.pem) if user logs in via keyfile (not required if password is provided)
36
+ # @param pwd [String] Password of user (not required if key is provided)
37
+ # @param cmd [Array<String>] List of valid shell command strings to be executed on host (required)
38
+ # @param verbose [Bool] console outputs are plotted to stdout/stderr if set (defaults to false)
39
+ #
40
+ # @return [Array<exit_code, stdout, stderr, command>] List of exit_code, stdout, stderr and executed commands
41
+ #
42
+ def self.exec4(host: '', user: '', port: 22, key: '', pwd: '', cmd: [], verbose: false)
43
+ keys = [key]
44
+ results = []
45
+
46
+ Net::SSH.start(host, user, port: port, password: pwd, keys: keys) do |ssh|
47
+ # Execute command by command
48
+ for command in cmd
49
+ stdout = ""
50
+ stderr = ""
51
+ code = nil
52
+ channel = ssh.open_channel do |ch|
53
+ ch.exec(command) do |c, success|
54
+ c.close unless success
55
+
56
+ c.on_data do |_, data|
57
+ stdout += data
58
+ $stdout.puts(data) if verbose
59
+ end
60
+
61
+ c.on_extended_data do |_, _, data|
62
+ stderr += data
63
+ $stderr.puts(data) if verbose
64
+ end
65
+
66
+ c.on_request('exit-status') { |_, data| code = data.read_long }
67
+ end
68
+ end
69
+ channel.wait
70
+ results << [code, stdout, stderr, command]
71
+
72
+ # If last command was not successful stop execution
73
+ if code != 0
74
+ ssh.close
75
+ return results
76
+ end
77
+ end
78
+ end
79
+
80
+ return results
81
+ end
82
+ end
data/open4ssh.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'open4ssh/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "open4ssh"
8
+ spec.version = Open4ssh::VERSION
9
+ spec.authors = ["Nane Kratzke"]
10
+ spec.email = ["nane.kratzke@fh-luebeck.de"]
11
+
12
+ spec.summary = %q{Wrapper around net-ssh for plain execution of remote shell commands.}
13
+ spec.description = %q{Wrapper around net-ssh for plain execution of remote shell commands and painless collection of exit codes, stdout, stderr messages.}
14
+ spec.homepage = "https://rubygems.org/gems/open4ssh"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "bin"
27
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.10"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "yard", "~> 0.8"
33
+
34
+ spec.add_runtime_dependency "net-ssh", "~> 3.2"
35
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open4ssh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nane Kratzke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
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.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: net-ssh
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ description: Wrapper around net-ssh for plain execution of remote shell commands and
70
+ painless collection of exit codes, stdout, stderr messages.
71
+ email:
72
+ - nane.kratzke@fh-luebeck.de
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/open4ssh.rb
84
+ - lib/open4ssh/version.rb
85
+ - open4ssh.gemspec
86
+ homepage: https://rubygems.org/gems/open4ssh
87
+ licenses:
88
+ - MIT
89
+ metadata:
90
+ allowed_push_host: https://rubygems.org
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.4.5.1
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Wrapper around net-ssh for plain execution of remote shell commands.
111
+ test_files: []
112
+ has_rdoc: