symphony-ssh 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.rdoc +113 -0
- data/lib/symphony/tasks/ssh.rb +166 -0
- data/lib/symphony/tasks/sshscript.rb +183 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ecf234436b7966c1062b9d13dbfe4c71015c29fe
|
4
|
+
data.tar.gz: 89636ab1155f42de42d0bc9f74e45f6b51299e18
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6b1681a726bbb2a554f2429c5dd11f3695e149c1341991088ad4deac99f0edd7728fcaca02480362cce98218dbe7b1e13dbfc1bcc01ef468df8199f3d9746df
|
7
|
+
data.tar.gz: bcc024b3faed110b348714ee2925d13544448da998f5a9d145ff1d017ed69c8b244047fe18019c6234aa8143c05aa3db1d5803e9430da9724cbd3808bf6d1df6
|
data/README.rdoc
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
= symphony-ssh
|
3
|
+
|
4
|
+
== Description
|
5
|
+
|
6
|
+
This is a small collection of base classes used for interacting with
|
7
|
+
remote machines over ssh. With them, you can use AMQP (via Symphony) to
|
8
|
+
run batch commands, execute templates as scripts, and perform any
|
9
|
+
batch/remoting stuff you can think of without the need of a separate
|
10
|
+
client agent.
|
11
|
+
|
12
|
+
These classes assume you have a user that can connect and login to
|
13
|
+
remote machines using a password-less ssh keypair. They are not meant
|
14
|
+
to be used directly. Subclass them!
|
15
|
+
|
16
|
+
See the rdoc for additional information and examples.
|
17
|
+
|
18
|
+
|
19
|
+
== Options
|
20
|
+
|
21
|
+
Symphony-ssh uses
|
22
|
+
Configurability[https://rubygems.org/gems/configurability] to determine
|
23
|
+
behavior. The configuration is a YAML[http://www.yaml.org/] file.
|
24
|
+
|
25
|
+
symphony_ssh:
|
26
|
+
path: /usr/bin/ssh
|
27
|
+
user: root
|
28
|
+
key: /path/to/a/private_key.rsa
|
29
|
+
opts:
|
30
|
+
- -e
|
31
|
+
- none
|
32
|
+
- -T
|
33
|
+
- -x
|
34
|
+
- -o
|
35
|
+
- CheckHostIP=no'
|
36
|
+
- -o
|
37
|
+
- BatchMode=yes'
|
38
|
+
- -o
|
39
|
+
- StrictHostKeyChecking=no
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
=== path
|
44
|
+
|
45
|
+
The absolute path to the ssh binary.
|
46
|
+
|
47
|
+
=== user
|
48
|
+
|
49
|
+
The default user to connect to remote hosts with. This can be
|
50
|
+
changes per connection in the AMQP payload.
|
51
|
+
|
52
|
+
=== key
|
53
|
+
|
54
|
+
An absolute path to a password-less ssh private key.
|
55
|
+
|
56
|
+
=== opts
|
57
|
+
|
58
|
+
SSH client options, passed to the ssh binary on the command line. Note
|
59
|
+
that the defaults have been tested fairly extensively, these are just
|
60
|
+
exposed if you have very specific needs and you know what you're doing.
|
61
|
+
|
62
|
+
|
63
|
+
== Installation
|
64
|
+
|
65
|
+
gem install symphony-ssh
|
66
|
+
|
67
|
+
|
68
|
+
== Contributing
|
69
|
+
|
70
|
+
You can check out the current development source with Mercurial via its
|
71
|
+
{project page}[http://bitbucket.org/mahlon/symphony-ssh].
|
72
|
+
|
73
|
+
After checking out the source, run:
|
74
|
+
|
75
|
+
$ rake
|
76
|
+
|
77
|
+
This task will run the tests/specs and generate the API documentation.
|
78
|
+
|
79
|
+
If you use {rvm}[http://rvm.io/], entering the project directory will
|
80
|
+
install any required development dependencies.
|
81
|
+
|
82
|
+
|
83
|
+
== License
|
84
|
+
|
85
|
+
Copyright (c) 2014, Mahlon E. Smith and Michael Granger
|
86
|
+
All rights reserved.
|
87
|
+
|
88
|
+
Redistribution and use in source and binary forms, with or without
|
89
|
+
modification, are permitted provided that the following conditions are met:
|
90
|
+
|
91
|
+
* Redistributions of source code must retain the above copyright notice,
|
92
|
+
this list of conditions and the following disclaimer.
|
93
|
+
|
94
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
95
|
+
this list of conditions and the following disclaimer in the documentation
|
96
|
+
and/or other materials provided with the distribution.
|
97
|
+
|
98
|
+
* Neither the name of the author/s, nor the names of the project's
|
99
|
+
contributors may be used to endorse or promote products derived from this
|
100
|
+
software without specific prior written permission.
|
101
|
+
|
102
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
103
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
104
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
105
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
106
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
107
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
108
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
109
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
110
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
111
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
112
|
+
|
113
|
+
|
@@ -0,0 +1,166 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
require 'shellwords'
|
5
|
+
require 'symphony/task' unless defined?( Symphony::Task )
|
6
|
+
|
7
|
+
|
8
|
+
### A base class for connecting to remote hosts, running arbitrary
|
9
|
+
### commands, and collecting output.
|
10
|
+
###
|
11
|
+
### This isn't designed to be used directly. To use this in your
|
12
|
+
### environment, you'll want to subclass it, add the behaviors
|
13
|
+
### that make sense for you, then super() back to the parent in the
|
14
|
+
### #work method.
|
15
|
+
###
|
16
|
+
### It expects the payload to contain the following keys:
|
17
|
+
###
|
18
|
+
### host: (required) The hostname to connect to
|
19
|
+
### command: (required) The command to run on the remote host
|
20
|
+
### port: (optional) The port to connect to (defaults to 22)
|
21
|
+
### opts: (optional) Explicit SSH client options
|
22
|
+
### user: (optional) The user to connect as (defaults to root)
|
23
|
+
### key: (optional) The path to an SSH private key
|
24
|
+
###
|
25
|
+
###
|
26
|
+
### Additionally, this class responds to the 'symphony_ssh' configurability
|
27
|
+
### key. Currently, you can set the 'path' argument, which is the
|
28
|
+
### full path to the local ssh binary (defaults to '/usr/bin/ssh') and
|
29
|
+
### override the default ssh user, key, and client opts.
|
30
|
+
###
|
31
|
+
### Textual output of the command is stored in the @output instance variable.
|
32
|
+
###
|
33
|
+
###
|
34
|
+
### require 'symphony'
|
35
|
+
### require 'symphony/tasks/ssh'
|
36
|
+
###
|
37
|
+
### class YourTask < Symphony::Task::SSH
|
38
|
+
### timeout 5
|
39
|
+
### subscribe_to 'ssh.command'
|
40
|
+
###
|
41
|
+
### def work( payload, metadata )
|
42
|
+
### status = super
|
43
|
+
### puts "Remote host said: %s" % [ @output ]
|
44
|
+
### return status.success?
|
45
|
+
### end
|
46
|
+
### end
|
47
|
+
###
|
48
|
+
class Symphony::Task::SSH < Symphony::Task
|
49
|
+
extend Configurability
|
50
|
+
config_key :symphony_ssh
|
51
|
+
|
52
|
+
# SSH default options.
|
53
|
+
#
|
54
|
+
CONFIG_DEFAULTS = {
|
55
|
+
:path => '/usr/bin/ssh',
|
56
|
+
:opts => [
|
57
|
+
'-e', 'none',
|
58
|
+
'-T',
|
59
|
+
'-x',
|
60
|
+
'-q',
|
61
|
+
'-o', 'CheckHostIP=no',
|
62
|
+
'-o', 'BatchMode=yes',
|
63
|
+
'-o', 'StrictHostKeyChecking=no'
|
64
|
+
],
|
65
|
+
:user => 'root',
|
66
|
+
:key => nil
|
67
|
+
}
|
68
|
+
|
69
|
+
# SSH "informative" stdout output that should be cleaned from the
|
70
|
+
# command output.
|
71
|
+
SSH_CLEANUP = %r/Warning: no access to tty|Thus no job control in this shell/
|
72
|
+
|
73
|
+
class << self
|
74
|
+
# The full path to the ssh binary.
|
75
|
+
attr_reader :path
|
76
|
+
|
77
|
+
# A default set of ssh client options when connecting
|
78
|
+
# to remote hosts.
|
79
|
+
attr_reader :opts
|
80
|
+
|
81
|
+
# The default user to use when connecting. If unset, 'root' is used.
|
82
|
+
attr_reader :user
|
83
|
+
|
84
|
+
# An absolute path to a password-free ssh private key.
|
85
|
+
attr_reader :key
|
86
|
+
end
|
87
|
+
|
88
|
+
### Configurability API.
|
89
|
+
###
|
90
|
+
def self::configure( config=nil )
|
91
|
+
config = self.defaults.merge( config || {} )
|
92
|
+
@path = config.delete( :path )
|
93
|
+
@opts = config.delete( :opts )
|
94
|
+
@user = config.delete( :user )
|
95
|
+
@key = config.delete( :key )
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
### Perform the ssh connection, passing the command to the pipe
|
101
|
+
### and retreiving any output from the remote end.
|
102
|
+
###
|
103
|
+
def work( payload, metadata )
|
104
|
+
command = payload[ 'command' ]
|
105
|
+
raise ArgumentError, "Missing required option 'command'" unless command
|
106
|
+
raise ArgumentError, "Missing required option 'host'" unless payload[ 'host' ]
|
107
|
+
|
108
|
+
exitcode = self.open_connection( payload, metadata ) do |reader, writer|
|
109
|
+
self.log.debug "Writing command #{command}..."
|
110
|
+
writer.puts( command )
|
111
|
+
self.log.debug " closing child's writer."
|
112
|
+
writer.close
|
113
|
+
self.log.debug " reading from child."
|
114
|
+
reader.read
|
115
|
+
end
|
116
|
+
|
117
|
+
self.log.debug "SSH exited: %d" % [ exitcode ]
|
118
|
+
return exitcode
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
#########
|
123
|
+
protected
|
124
|
+
#########
|
125
|
+
|
126
|
+
### Call ssh and yield the remote IO objects to the caller,
|
127
|
+
### cleaning up afterwards.
|
128
|
+
###
|
129
|
+
def open_connection( payload, metadata=nil )
|
130
|
+
raise LocalJumpError, "no block given" unless block_given?
|
131
|
+
@output = ''
|
132
|
+
|
133
|
+
port = payload[ 'port' ] || 22
|
134
|
+
opts = payload[ 'opts' ] || Symphony::Task::SSH.opts
|
135
|
+
user = payload[ 'user' ] || Symphony::Task::SSH.user
|
136
|
+
key = payload[ 'key' ] || Symphony::Task::SSH.key
|
137
|
+
|
138
|
+
cmd = []
|
139
|
+
cmd << Symphony::Task::SSH.path
|
140
|
+
cmd += Symphony::Task::SSH.opts
|
141
|
+
|
142
|
+
cmd << '-p' << port.to_s
|
143
|
+
cmd << '-i' << key if key
|
144
|
+
cmd << '-l' << user
|
145
|
+
cmd << payload[ 'host' ]
|
146
|
+
cmd.flatten!
|
147
|
+
self.log.debug "Running SSH command with: %p" % [ Shellwords.shelljoin(cmd) ]
|
148
|
+
|
149
|
+
parent_reader, child_writer = IO.pipe
|
150
|
+
child_reader, parent_writer = IO.pipe
|
151
|
+
|
152
|
+
pid = spawn( *cmd, :out => child_writer, :in => child_reader, :close_others => true )
|
153
|
+
child_writer.close
|
154
|
+
child_reader.close
|
155
|
+
|
156
|
+
self.log.debug "Yielding back to the run block."
|
157
|
+
@output = yield( parent_reader, parent_writer )
|
158
|
+
@output = @output.split("\n").reject{|l| l =~ SSH_CLEANUP }.join
|
159
|
+
self.log.debug " run block done."
|
160
|
+
|
161
|
+
pid, status = Process.waitpid2( pid )
|
162
|
+
return status
|
163
|
+
end
|
164
|
+
|
165
|
+
end # Symphony::Task::SSH
|
166
|
+
|
@@ -0,0 +1,183 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
require 'net/ssh'
|
5
|
+
require 'net/sftp'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'inversion'
|
8
|
+
require 'symphony'
|
9
|
+
require 'symphony/task'
|
10
|
+
|
11
|
+
|
12
|
+
### A base class for connecting to a remote host, then uploading and
|
13
|
+
### executing an Inversion templated script.
|
14
|
+
###
|
15
|
+
### This isn't designed to be used directly. To use this in your
|
16
|
+
### environment, you'll want to subclass it, add the behaviors
|
17
|
+
### that make sense for you, then super() back to the parent in the
|
18
|
+
### #work method.
|
19
|
+
###
|
20
|
+
### It expects the payload to contain the following keys:
|
21
|
+
###
|
22
|
+
### host: (required) The hostname to connect to
|
23
|
+
### template: (required) A path to the Inversion templated script
|
24
|
+
### port: (optional) The port to connect to (defaults to 22)
|
25
|
+
### user: (optional) The user to connect as (defaults to root)
|
26
|
+
### key: (optional) The path to an SSH private key
|
27
|
+
### attributes: (optional) Additional data to attach to the template
|
28
|
+
### nocleanup: (optional) Leave the remote script after execution? (default to false)
|
29
|
+
###
|
30
|
+
###
|
31
|
+
### Additionally, this class responds to the 'symphony_ssh' configurability
|
32
|
+
### key. Currently, you can override the default ssh user and private key.
|
33
|
+
###
|
34
|
+
### Textual output of the command is stored in the @output instance variable.
|
35
|
+
###
|
36
|
+
###
|
37
|
+
### require 'symphony'
|
38
|
+
### require 'symphony/tasks/sshscript'
|
39
|
+
###
|
40
|
+
### class YourTask < Symphony::Task::SSHScript
|
41
|
+
### timeout 30
|
42
|
+
### subscribe_to 'ssh.script.*'
|
43
|
+
###
|
44
|
+
### def work( payload, metadata )
|
45
|
+
### status = super
|
46
|
+
### puts "Remote script said: %s" % [ @output ]
|
47
|
+
### return status.success?
|
48
|
+
### end
|
49
|
+
### end
|
50
|
+
###
|
51
|
+
class Symphony::Task::SSHScript < Symphony::Task
|
52
|
+
extend Configurability
|
53
|
+
config_key :symphony_ssh
|
54
|
+
|
55
|
+
# Template config
|
56
|
+
#
|
57
|
+
TEMPLATE_OPTS = {
|
58
|
+
:ignore_unknown_tags => false,
|
59
|
+
:on_render_error => :propagate,
|
60
|
+
:strip_tag_lines => true
|
61
|
+
}
|
62
|
+
|
63
|
+
# The defaults to use when connecting via SSH
|
64
|
+
#
|
65
|
+
DEFAULT_SSH_OPTIONS = {
|
66
|
+
:auth_methods => [ 'publickey' ],
|
67
|
+
:compression => true,
|
68
|
+
:config => false,
|
69
|
+
:keys_only => true,
|
70
|
+
:paranoid => false,
|
71
|
+
:global_known_hosts_file => '/dev/null',
|
72
|
+
:user_known_hosts_file => '/dev/null'
|
73
|
+
}
|
74
|
+
|
75
|
+
# SSH default options.
|
76
|
+
#
|
77
|
+
CONFIG_DEFAULTS = {
|
78
|
+
:user => 'root',
|
79
|
+
:key => nil
|
80
|
+
}
|
81
|
+
|
82
|
+
class << self
|
83
|
+
# The default user to use when connecting. If unset, 'root' is used.
|
84
|
+
attr_reader :user
|
85
|
+
|
86
|
+
# An absolute path to a password-free ssh private key.
|
87
|
+
attr_reader :key
|
88
|
+
end
|
89
|
+
|
90
|
+
### Configurability API.
|
91
|
+
###
|
92
|
+
def self::configure( config=nil )
|
93
|
+
config = self.defaults.merge( config || {} )
|
94
|
+
@user = config.delete( :user )
|
95
|
+
@key = config.delete( :key )
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
### Perform the ssh connection, render the template, send it, and
|
101
|
+
### execute it.
|
102
|
+
###
|
103
|
+
def work( payload, metadata )
|
104
|
+
template = payload[ 'template' ]
|
105
|
+
attributes = payload[ 'attributes' ] || {}
|
106
|
+
port = payload[ 'port' ] || 22
|
107
|
+
user = payload[ 'user' ] || Symphony::Task::SSHScript.user
|
108
|
+
key = payload[ 'key' ] || Symphony::Task::SSHScript.key
|
109
|
+
nocleanup = payload[ 'nocleanup' ]
|
110
|
+
|
111
|
+
raise ArgumentError, "Missing required option 'command'" unless template
|
112
|
+
raise ArgumentError, "Missing required option 'host'" unless payload[ 'host' ]
|
113
|
+
|
114
|
+
remote_filename = self.make_remote_filename( template )
|
115
|
+
source = self.generate_script( template, attributes )
|
116
|
+
|
117
|
+
ssh_options = DEFAULT_SSH_OPTIONS.merge( :port => port, :keys => [key] )
|
118
|
+
ssh_options.merge!(
|
119
|
+
:logger => Loggability[ Net::SSH ],
|
120
|
+
:verbose => :debug
|
121
|
+
) if payload[ 'debug' ]
|
122
|
+
|
123
|
+
Net::SSH.start( payload['host'], user, ssh_options ) do |conn|
|
124
|
+
self.log.debug "Uploading script (%d bytes) to %s:%s." %
|
125
|
+
[ source.bytesize, payload['host'], remote_filename ]
|
126
|
+
self.upload_script( conn, source, remote_filename )
|
127
|
+
self.log.debug " done with the upload."
|
128
|
+
|
129
|
+
self.run_script( conn, remote_filename, nocleanup )
|
130
|
+
self.log.debug "Output was:\n#{@output}"
|
131
|
+
end
|
132
|
+
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
#########
|
138
|
+
protected
|
139
|
+
#########
|
140
|
+
|
141
|
+
### Generate a unique filename for the script on the remote host,
|
142
|
+
### based on +template+ name.
|
143
|
+
###
|
144
|
+
def make_remote_filename( template )
|
145
|
+
basename = File.basename( template, File.extname(template) )
|
146
|
+
tmpname = Dir::Tmpname.make_tmpname( basename, rand(10000) )
|
147
|
+
|
148
|
+
return "/tmp/#{tmpname}"
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
### Generate a script by loading the script +template+, populating it with
|
153
|
+
### +attributes+, and returning the rendered output.
|
154
|
+
###
|
155
|
+
def generate_script( template, attributes )
|
156
|
+
tmpl = Inversion::Template.load( template, TEMPLATE_OPTS )
|
157
|
+
tmpl.attributes.merge!( attributes )
|
158
|
+
tmpl.task = self
|
159
|
+
|
160
|
+
return tmpl.render
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
### Upload the templated +source+ via the ssh +conn+ to an
|
165
|
+
### executable file named +remote_filename+.
|
166
|
+
###
|
167
|
+
def upload_script( conn, source, remote_filename )
|
168
|
+
conn.sftp.file.open( remote_filename, "w", 0755 ) do |fh|
|
169
|
+
fh.print( source )
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
### Run the +remote_filename+ via the ssh +conn+. The script
|
175
|
+
### will be deleted automatically unless +nocleanup+ is true.
|
176
|
+
###
|
177
|
+
def run_script( conn, remote_filename, nocleanup=false )
|
178
|
+
@output = conn.exec!( remote_filename )
|
179
|
+
conn.exec!( "rm #{remote_filename}" ) unless nocleanup
|
180
|
+
end
|
181
|
+
|
182
|
+
end # Symphony::Task::SSHScript
|
183
|
+
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: symphony-ssh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mahlon E. Smith <mahlon@martini.nu>
|
8
|
+
- Michael Granger <ged@faeriemud.org>
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: symphony
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.6'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.6'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: net-ssh
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '2.9'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '2.9'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: net-sftp
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.1'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2.1'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '3.0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: simplecov
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.8'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.8'
|
84
|
+
description: |
|
85
|
+
A small collection of base classes used for interacting with remote
|
86
|
+
machines over ssh. With them, you can use AMQP (via Symphony) to
|
87
|
+
run batch commands, execute templates as scripts, and perform any
|
88
|
+
batch/remoting stuff you can think of without the need of separate
|
89
|
+
client agents.
|
90
|
+
email: mahlon@martini.nu
|
91
|
+
executables: []
|
92
|
+
extensions: []
|
93
|
+
extra_rdoc_files: []
|
94
|
+
files:
|
95
|
+
- README.rdoc
|
96
|
+
- lib/symphony/tasks/ssh.rb
|
97
|
+
- lib/symphony/tasks/sshscript.rb
|
98
|
+
homepage: http://projects.martini.nu/ruby-modules
|
99
|
+
licenses:
|
100
|
+
- BSD
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.0.0
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: 2.0.3
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.2.2
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Base classes for using Symphony with ssh.
|
122
|
+
test_files: []
|