mobilize-ssh 1.1.10 → 1.2
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.
- 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
|