mobilize-ssh 1.1.10 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -10
- data/lib/mobilize-ssh/extensions/net-ssh-connection-session.rb +29 -8
- data/lib/mobilize-ssh/extensions/net-ssh-gateway.rb +31 -9
- data/lib/mobilize-ssh/handlers/ssh.rb +110 -38
- data/lib/mobilize-ssh/version.rb +1 -1
- data/lib/samples/ssh.yml +0 -3
- data/mobilize-ssh.gemspec +2 -2
- data/test/code2.sh +1 -0
- data/test/mobilize-ssh_test.rb +30 -2
- data/test/ssh_job_rows.yml +5 -5
- metadata +7 -5
data/README.md
CHANGED
@@ -84,7 +84,7 @@ over to the nodes. They will be deleted afterwards, unless the job
|
|
84
84
|
fails in mid-copy. By default this is tmp/file/.
|
85
85
|
* nodes, identified by aliases, such as `test_node`. This alias is what you should
|
86
86
|
pass into the "node" param over in the ssh.run task.
|
87
|
-
*
|
87
|
+
* if no node is specified, commands will default to the first node listed.
|
88
88
|
|
89
89
|
Each node has:
|
90
90
|
* a host;
|
@@ -110,7 +110,6 @@ Sample ssh.yml:
|
|
110
110
|
---
|
111
111
|
development:
|
112
112
|
tmp_file_dir: tmp/file/
|
113
|
-
default_node: dev_node
|
114
113
|
nodes:
|
115
114
|
dev_node:
|
116
115
|
sudoers:
|
@@ -128,7 +127,6 @@ development:
|
|
128
127
|
user: gateway_user
|
129
128
|
test:
|
130
129
|
tmp_file_dir: tmp/file/
|
131
|
-
default_node: test_node
|
132
130
|
nodes:
|
133
131
|
test_node:
|
134
132
|
sudoers:
|
@@ -146,7 +144,6 @@ test:
|
|
146
144
|
user: gateway_user
|
147
145
|
production:
|
148
146
|
tmp_file_dir: tmp/file/
|
149
|
-
default_node: prod_node
|
150
147
|
nodes:
|
151
148
|
prod_node:
|
152
149
|
sudoers:
|
@@ -172,13 +169,15 @@ Start
|
|
172
169
|
### Create Job
|
173
170
|
|
174
171
|
* For mobilize-ssh, the following task is available:
|
175
|
-
* ssh.run `node: <node_alias>, cmd: <command>, user: user, sources:[*<
|
176
|
-
all gsheets, copies them to a temporary folder on the selected node, and
|
177
|
-
runs the command inside that folder.
|
172
|
+
* ssh.run `node: <node_alias>, cmd: <command>, user: user, sources:[*<source_paths>]`, which reads sources, copies them to a temporary folder on the selected node, and runs the command inside that folder.
|
178
173
|
* user, sources, and node are optional; cmd is required.
|
179
|
-
* specifying user will cause the command to be prefixed with sudo su <user> -c.
|
180
|
-
|
181
|
-
*
|
174
|
+
* specifying user will cause the command to be prefixed with sudo su <user> -c.
|
175
|
+
* non-google sources will also be read as the specified user.
|
176
|
+
* not specifying node will cause the command to be run on the default node.
|
177
|
+
* ssh sources can be specified with syntax
|
178
|
+
`ssh://<node><file_full_path>`. If node is omitted, default node will be used.
|
179
|
+
* `<node><file_full_path>` and `<file_full_path>` can be used in the context of ssh.run, but if the path has only 1 slash, or none, it will try to find a google sheet or file instead.
|
180
|
+
* The test uses `ssh.run node:"test_node", cmd:"ruby code.rb", user: "root", sources:["code.rb","code.sh"]`
|
182
181
|
|
183
182
|
<a name='section_Start_Run_Test'></a>
|
184
183
|
### Run Test
|
@@ -1,14 +1,35 @@
|
|
1
1
|
class Net::SSH::Connection::Session
|
2
|
+
#from http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library
|
2
3
|
def run(command)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
ssh = self
|
5
|
+
stdout_data = ""
|
6
|
+
stderr_data = ""
|
7
|
+
exit_code = nil
|
8
|
+
exit_signal = nil
|
9
|
+
ssh.open_channel do |channel|
|
10
|
+
channel.exec(command) do |chan, success|
|
11
|
+
unless success
|
12
|
+
abort "FAILED: couldn't execute command (ssh.channel.exec)"
|
13
|
+
end
|
14
|
+
channel.on_data do |ch,data|
|
15
|
+
stdout_data+=data
|
16
|
+
end
|
17
|
+
|
18
|
+
channel.on_extended_data do |ch,type,data|
|
19
|
+
stderr_data+=data
|
20
|
+
end
|
21
|
+
|
22
|
+
channel.on_request("exit-status") do |ch,data|
|
23
|
+
exit_code = data.read_long
|
24
|
+
end
|
25
|
+
|
26
|
+
channel.on_request("exit-signal") do |ch, data|
|
27
|
+
exit_signal = data.read_long
|
28
|
+
end
|
9
29
|
end
|
10
30
|
end
|
11
|
-
|
12
|
-
|
31
|
+
ssh.loop
|
32
|
+
response = {'stdout'=>stdout_data, 'stderr'=>stderr_data, 'exit_code'=>exit_code, 'exit_signal'=>exit_signal}
|
33
|
+
response
|
13
34
|
end
|
14
35
|
end
|
@@ -1,18 +1,40 @@
|
|
1
1
|
class Net::SSH::Gateway
|
2
2
|
def self.run(gname,guser,name,user,command,gopts={},opts={})
|
3
|
-
|
3
|
+
gate = self
|
4
|
+
gateway = gate.new(gname,guser,gopts)
|
5
|
+
response = nil
|
4
6
|
gateway.ssh(name,user,opts) do |ssh|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
#from http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library
|
8
|
+
stdout_data = ""
|
9
|
+
stderr_data = ""
|
10
|
+
exit_code = nil
|
11
|
+
exit_signal = nil
|
12
|
+
ssh.open_channel do |channel|
|
13
|
+
channel.exec(command) do |chan, success|
|
14
|
+
unless success
|
15
|
+
abort "FAILED: couldn't execute command (ssh.channel.exec)"
|
16
|
+
end
|
17
|
+
channel.on_data do |ch,data|
|
18
|
+
stdout_data+=data
|
19
|
+
end
|
20
|
+
|
21
|
+
channel.on_extended_data do |ch,type,data|
|
22
|
+
stderr_data+=data
|
23
|
+
end
|
24
|
+
|
25
|
+
channel.on_request("exit-status") do |ch,data|
|
26
|
+
exit_code = data.read_long
|
27
|
+
end
|
28
|
+
|
29
|
+
channel.on_request("exit-signal") do |ch, data|
|
30
|
+
exit_signal = data.read_long
|
31
|
+
end
|
11
32
|
end
|
12
33
|
end
|
13
|
-
|
14
|
-
|
34
|
+
ssh.loop
|
35
|
+
response = {'stdout'=>stdout_data, 'stderr'=>stderr_data, 'exit_code'=>exit_code, 'exit_signal'=>exit_signal}
|
15
36
|
end
|
37
|
+
response
|
16
38
|
end
|
17
39
|
def self.sync(gname,guser,name,user,from_path,to_path,gopts={},opts={})
|
18
40
|
gateway = self.new(gname,guser,gopts)
|
@@ -24,8 +24,12 @@ module Mobilize
|
|
24
24
|
Ssh.config['nodes'][node]['su_all_users']
|
25
25
|
end
|
26
26
|
|
27
|
+
def Ssh.nodes
|
28
|
+
Ssh.config['nodes'].keys
|
29
|
+
end
|
30
|
+
|
27
31
|
def Ssh.default_node
|
28
|
-
Ssh.
|
32
|
+
Ssh.nodes.first
|
29
33
|
end
|
30
34
|
|
31
35
|
#determine if current machine is on host domain, needs gateway if one is provided and it is not
|
@@ -53,6 +57,43 @@ module Mobilize
|
|
53
57
|
return true
|
54
58
|
end
|
55
59
|
|
60
|
+
# converts a source path or target path to a dst in the context of handler and stage
|
61
|
+
def Ssh.path_to_dst(path,stage_path)
|
62
|
+
has_handler = true if path.index("://")
|
63
|
+
red_path = path.split("://").last
|
64
|
+
#is user has a handler, their first path node is a node name,
|
65
|
+
#or there are more than 2 path nodes, try to find Ssh file
|
66
|
+
if has_handler or Ssh.nodes.include?(red_path.split("/").first) or red_path.split("/").length > 2
|
67
|
+
user_name = Ssh.user_name_by_stage_path(stage_path)
|
68
|
+
ssh_url = Ssh.url_by_path(red_path,user_name)
|
69
|
+
return Dataset.find_or_create_by_url(ssh_url)
|
70
|
+
end
|
71
|
+
#otherwise, use Gsheet
|
72
|
+
return Gsheet.path_to_dst(red_path,stage_path)
|
73
|
+
end
|
74
|
+
|
75
|
+
def Ssh.url_by_path(path,user_name)
|
76
|
+
node = path.split("/").first.to_s
|
77
|
+
if Ssh.nodes.include?(node)
|
78
|
+
#cut node out of path
|
79
|
+
path = "/" + path.split("/")[1..-1].join("/")
|
80
|
+
else
|
81
|
+
node = Ssh.default_node
|
82
|
+
path = path.starts_with?("/") ? path : "/#{path}"
|
83
|
+
end
|
84
|
+
url = "ssh://#{node}#{path}"
|
85
|
+
begin
|
86
|
+
response = Ssh.run(node, "head -1 #{path}", user_name)
|
87
|
+
if response['exit_code'] != 0
|
88
|
+
raise "Unable to find #{url} with error: #{response['stderr']}"
|
89
|
+
else
|
90
|
+
return "ssh://#{node}#{path}"
|
91
|
+
end
|
92
|
+
rescue => exc
|
93
|
+
raise Exception, "Unable to find #{url} with error: #{exc.to_s}", exc.backtrace
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
56
97
|
def Ssh.scp(node,from_path,to_path)
|
57
98
|
name,key,port,user = Ssh.host(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
|
58
99
|
key_path = "#{Base.root}/#{key}"
|
@@ -119,20 +160,29 @@ module Mobilize
|
|
119
160
|
key_path = "#{Base.root}/#{key}"
|
120
161
|
Ssh.set_key_permissions(key_path)
|
121
162
|
opts = {:port=>(port || 22),:keys=>key_path}
|
122
|
-
if Ssh.needs_gateway?(node)
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
163
|
+
response = if Ssh.needs_gateway?(node)
|
164
|
+
gname,gkey,gport,guser = Ssh.gateway(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
|
165
|
+
gkey_path = "#{Base.root}/#{gkey}"
|
166
|
+
gopts = {:port=>(gport || 22),:keys=>gkey_path}
|
167
|
+
Net::SSH::Gateway.run(gname,guser,name,user,cmd,gopts,opts)
|
168
|
+
else
|
169
|
+
Net::SSH.start(name,user,opts) do |ssh|
|
170
|
+
ssh.run(cmd)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
response
|
132
174
|
end
|
133
175
|
|
134
|
-
def Ssh.
|
135
|
-
|
176
|
+
def Ssh.read_by_dataset_path(dst_path,user_name,*args)
|
177
|
+
#expects node as first part of path
|
178
|
+
node,path = dst_path.split("/").ie{|pa| [pa.first,pa[1..-1].join("/")]}
|
179
|
+
#slash in front of path
|
180
|
+
response = Ssh.run(node,"cat /#{path}",user_name)
|
181
|
+
if response['exit_code'] == 0
|
182
|
+
return response['stdout']
|
183
|
+
else
|
184
|
+
raise "Unable to read ssh://#{dst_path} with error: #{response['stderr']}"
|
185
|
+
end
|
136
186
|
end
|
137
187
|
|
138
188
|
def Ssh.write(node,fdata,to_path,binary=false)
|
@@ -154,37 +204,59 @@ module Mobilize
|
|
154
204
|
return tmp_file_path
|
155
205
|
end
|
156
206
|
|
157
|
-
def Ssh.
|
207
|
+
def Ssh.user_name_by_stage_path(stage_path,node=nil)
|
158
208
|
s = Stage.where(:path=>stage_path).first
|
159
209
|
u = s.job.runner.user
|
210
|
+
user_name = s.params['user']
|
211
|
+
node = s.params['node']
|
212
|
+
node = Ssh.default_node unless Ssh.nodes.include?(node)
|
213
|
+
if user_name and !Ssh.sudoers(node).include?(u.name)
|
214
|
+
raise "#{u.name} does not have su permissions for this node"
|
215
|
+
elsif user_name.nil? and Ssh.su_all_users(node)
|
216
|
+
user_name = u.name
|
217
|
+
end
|
218
|
+
return user_name
|
219
|
+
end
|
220
|
+
|
221
|
+
def Ssh.file_hash_by_stage_path(stage_path)
|
222
|
+
file_hash = {}
|
223
|
+
s = Stage.where(:path=>stage_path).first
|
224
|
+
u = s.job.runner.user
|
225
|
+
user_name = Ssh.user_name_by_stage_path(stage_path)
|
226
|
+
s.sources.each do |sdst|
|
227
|
+
split_path = sdst.path.split("/")
|
228
|
+
#if path is to stage output, name with stage name
|
229
|
+
file_name = if split_path.last == "out" and
|
230
|
+
(1..5).to_a.map{|n| "stage#{n.to_s}"}.include?(split_path[-2].to_s)
|
231
|
+
"#{split_path[-2]}.out"
|
232
|
+
else
|
233
|
+
split_path.last
|
234
|
+
end
|
235
|
+
if ["gsheet","gfile"].include?(sdst.handler)
|
236
|
+
#google drive sources are always read as the user
|
237
|
+
file_hash[file_name] = sdst.read(u.name)
|
238
|
+
else
|
239
|
+
#other sources should be read by su-user
|
240
|
+
file_hash[file_name] = sdst.read(user_name)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
return file_hash
|
244
|
+
end
|
245
|
+
|
246
|
+
def Ssh.run_by_stage_path(stage_path)
|
247
|
+
s = Stage.where(:path=>stage_path).first
|
160
248
|
params = s.params
|
161
249
|
node, command = [params['node'],params['cmd']]
|
162
250
|
node ||= Ssh.default_node
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
s.source_dsts(gdrive_slot).each do |sdst|
|
167
|
-
split_path = sdst.path.split("/")
|
168
|
-
#if path is to stage output, name with stage name
|
169
|
-
file_name = if split_path.last == "out" and
|
170
|
-
(1..5).to_a.map{|n| "stage#{n.to_s}"}.include?(split_path[-2].to_s)
|
171
|
-
"#{split_path[-2]}.out"
|
172
|
-
else
|
173
|
-
split_path.last
|
174
|
-
end
|
175
|
-
file_hash[file_name] = sdst.read(u.name)
|
176
|
-
end
|
177
|
-
Gdrive.unslot_worker_by_path(s.path)
|
178
|
-
user = s.params['user']
|
179
|
-
if user and !Ssh.sudoers(node).include?(u.name)
|
180
|
-
raise "#{u.name} does not have su permissions for this node"
|
181
|
-
elsif user.nil? and Ssh.su_all_users(node)
|
182
|
-
user = u.name
|
183
|
-
end
|
184
|
-
out_tsv = Ssh.run(node,command,user,file_hash)
|
251
|
+
user_name = Ssh.user_name_by_stage_path(stage_path)
|
252
|
+
file_hash = Ssh.file_hash_by_stage_path(stage_path)
|
253
|
+
result = Ssh.run(node,command,user_name,file_hash)
|
185
254
|
#use Gridfs to cache result
|
186
|
-
|
187
|
-
Dataset.write_by_url(
|
255
|
+
response = {}
|
256
|
+
response['out_url'] = Dataset.write_by_url("gridfs://#{s.path}/out",result['stdout'].to_s,Gdrive.owner_name)
|
257
|
+
response['err_url'] = Dataset.write_by_url("gridfs://#{s.path}/err",result['stderr'].to_s,Gdrive.owner_name) if result['stderr'].to_s.length>0
|
258
|
+
response['signal'] = result['exit_code']
|
259
|
+
response
|
188
260
|
end
|
189
261
|
end
|
190
262
|
end
|
data/lib/mobilize-ssh/version.rb
CHANGED
data/lib/samples/ssh.yml
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
---
|
2
2
|
development:
|
3
3
|
tmp_file_dir: tmp/file/
|
4
|
-
default_node: dev_node
|
5
4
|
nodes:
|
6
5
|
dev_node:
|
7
6
|
sudoers:
|
@@ -19,7 +18,6 @@ development:
|
|
19
18
|
user: gateway_user
|
20
19
|
test:
|
21
20
|
tmp_file_dir: tmp/file/
|
22
|
-
default_node: test_node
|
23
21
|
nodes:
|
24
22
|
test_node:
|
25
23
|
sudoers:
|
@@ -37,7 +35,6 @@ test:
|
|
37
35
|
user: gateway_user
|
38
36
|
production:
|
39
37
|
tmp_file_dir: tmp/file/
|
40
|
-
default_node: prod_node
|
41
38
|
nodes:
|
42
39
|
prod_node:
|
43
40
|
sudoers:
|
data/mobilize-ssh.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.name = "mobilize-ssh"
|
8
8
|
gem.version = Mobilize::Ssh::VERSION
|
9
9
|
gem.authors = ["Cassio Paes-Leme"]
|
10
|
-
gem.email = ["cpaesleme@
|
10
|
+
gem.email = ["cpaesleme@dena.com"]
|
11
11
|
gem.description = %q{mobilize-ssh allows you to automate ssh commands and files across hosts}
|
12
12
|
gem.summary = %q{extend mobilize-base with the ability to run files across hosts}
|
13
13
|
gem.homepage = "http://github.com/dena/mobilize-ssh"
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
|
-
gem.add_runtime_dependency "mobilize-base","1.
|
19
|
+
gem.add_runtime_dependency "mobilize-base","1.2"
|
20
20
|
gem.add_runtime_dependency "net-ssh"
|
21
21
|
gem.add_runtime_dependency "net-scp"
|
22
22
|
gem.add_runtime_dependency "net-ssh-gateway"
|
data/test/code2.sh
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
tail syslog
|
data/test/mobilize-ssh_test.rb
CHANGED
@@ -20,6 +20,7 @@ describe "Mobilize" do
|
|
20
20
|
|
21
21
|
rb_code_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/code.rb",gdrive_slot)
|
22
22
|
sh_code_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/code.sh",gdrive_slot)
|
23
|
+
sh_code_sheet2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/code2.sh",gdrive_slot)
|
23
24
|
[rb_code_sheet,sh_code_sheet].each {|s| s.delete if s}
|
24
25
|
|
25
26
|
puts "add test code"
|
@@ -31,6 +32,10 @@ describe "Mobilize" do
|
|
31
32
|
sh_code_tsv = File.open("#{Mobilize::Base.root}/test/code.sh").read
|
32
33
|
sh_code_sheet.write(sh_code_tsv,Mobilize::Gdrive.owner_name)
|
33
34
|
|
35
|
+
sh_code_sheet2 = Mobilize::Gsheet.find_or_create_by_path("#{r.path.split("/")[0..-2].join("/")}/code2.sh",gdrive_slot)
|
36
|
+
sh_code_tsv2 = File.open("#{Mobilize::Base.root}/test/code2.sh").read
|
37
|
+
sh_code_sheet2.write(sh_code_tsv2,Mobilize::Gdrive.owner_name)
|
38
|
+
|
34
39
|
jobs_sheet = r.gsheet(gdrive_slot)
|
35
40
|
|
36
41
|
#delete target sheets if they exist
|
@@ -43,9 +48,9 @@ describe "Mobilize" do
|
|
43
48
|
ssh_job_rows.map{|j| r.jobs(j['name'])}.each{|j| j.delete if j}
|
44
49
|
jobs_sheet.add_or_update_rows(ssh_job_rows)
|
45
50
|
|
46
|
-
puts "job row added, force enqueue runner, wait
|
51
|
+
puts "job row added, force enqueue runner, wait for stages"
|
47
52
|
r.enqueue!
|
48
|
-
|
53
|
+
wait_for_stages(900)
|
49
54
|
|
50
55
|
puts "update job status and activity"
|
51
56
|
r.update_gsheet(gdrive_slot)
|
@@ -61,4 +66,27 @@ describe "Mobilize" do
|
|
61
66
|
|
62
67
|
end
|
63
68
|
|
69
|
+
def wait_for_stages(time_limit=600,stage_limit=120,wait_length=10)
|
70
|
+
time = 0
|
71
|
+
time_since_stage = 0
|
72
|
+
#check for 10 min
|
73
|
+
while time < time_limit and time_since_stage < stage_limit
|
74
|
+
sleep wait_length
|
75
|
+
job_classes = Mobilize::Resque.jobs.map{|j| j['class']}
|
76
|
+
if job_classes.include?("Mobilize::Stage")
|
77
|
+
time_since_stage = 0
|
78
|
+
puts "saw stage at #{time.to_s} seconds"
|
79
|
+
else
|
80
|
+
time_since_stage += wait_length
|
81
|
+
puts "#{time_since_stage.to_s} seconds since stage seen"
|
82
|
+
end
|
83
|
+
time += wait_length
|
84
|
+
puts "total wait time #{time.to_s} seconds"
|
85
|
+
end
|
86
|
+
|
87
|
+
if time >= time_limit
|
88
|
+
raise "Timed out before stage completion"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
64
92
|
end
|
data/test/ssh_job_rows.yml
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
active: true
|
3
3
|
trigger: once
|
4
4
|
status: ""
|
5
|
-
stage1: 'ssh.run node:"test_node", cmd:"ruby code.rb", user:"root", sources:["
|
6
|
-
stage2: 'gsheet.write source:"stage1", target:"
|
5
|
+
stage1: 'ssh.run node:"test_node", cmd:"ruby code.rb", user:"root", sources:["code.rb", "code.sh"]'
|
6
|
+
stage2: 'gsheet.write source:"stage1", target:"test_ssh_1.out"'
|
7
7
|
- name: test_ssh_2
|
8
8
|
active: true
|
9
9
|
trigger: "after test_ssh_1"
|
10
10
|
status: ""
|
11
|
-
stage1: 'ssh.run cmd:"sh
|
12
|
-
stage2: 'gsheet.write source:"stage1", target:"
|
11
|
+
stage1: 'ssh.run cmd:"sh code2.sh", user:"root", sources:["code2.sh","test_node/var/log/syslog"]'
|
12
|
+
stage2: 'gsheet.write source:"stage1", target:"test_ssh_2.out"'
|
13
13
|
- name: test_ssh_3
|
14
14
|
active: true
|
15
15
|
trigger: "after test_ssh_2"
|
16
16
|
status: ""
|
17
17
|
stage1: 'ssh.run cmd:"whoami"'
|
18
|
-
stage2: 'gsheet.write source:"stage1", target:"
|
18
|
+
stage2: 'gsheet.write source:"stage1", target:"test_ssh_3.out"'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobilize-ssh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: '1.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mobilize-base
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - '='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 1.
|
21
|
+
version: '1.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - '='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 1.
|
29
|
+
version: '1.2'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: net-ssh
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,7 +77,7 @@ dependencies:
|
|
77
77
|
version: '0'
|
78
78
|
description: mobilize-ssh allows you to automate ssh commands and files across hosts
|
79
79
|
email:
|
80
|
-
- cpaesleme@
|
80
|
+
- cpaesleme@dena.com
|
81
81
|
executables: []
|
82
82
|
extensions: []
|
83
83
|
extra_rdoc_files: []
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- mobilize-ssh.gemspec
|
100
100
|
- test/code.rb
|
101
101
|
- test/code.sh
|
102
|
+
- test/code2.sh
|
102
103
|
- test/mobilize-ssh_test.rb
|
103
104
|
- test/redis-test.conf
|
104
105
|
- test/ssh_job_rows.yml
|
@@ -130,6 +131,7 @@ summary: extend mobilize-base with the ability to run files across hosts
|
|
130
131
|
test_files:
|
131
132
|
- test/code.rb
|
132
133
|
- test/code.sh
|
134
|
+
- test/code2.sh
|
133
135
|
- test/mobilize-ssh_test.rb
|
134
136
|
- test/redis-test.conf
|
135
137
|
- test/ssh_job_rows.yml
|