scout-gear 8.0.0 → 8.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 +4 -4
- data/.vimproject +26 -9
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/scout +15 -4
- data/doc/lib/scout/path.md +35 -0
- data/doc/lib/scout/workflow/task.md +13 -0
- data/lib/scout/cmd.rb +23 -24
- data/lib/scout/concurrent_stream.rb +36 -19
- data/lib/scout/exceptions.rb +10 -0
- data/lib/scout/log/color.rb +11 -11
- data/lib/scout/log/progress/report.rb +7 -5
- data/lib/scout/log/progress/util.rb +3 -0
- data/lib/scout/log/trap.rb +3 -3
- data/lib/scout/log.rb +64 -36
- data/lib/scout/meta_extension.rb +34 -0
- data/lib/scout/misc/digest.rb +11 -2
- data/lib/scout/misc/format.rb +12 -7
- data/lib/scout/misc/monitor.rb +11 -0
- data/lib/scout/misc/system.rb +48 -0
- data/lib/scout/named_array.rb +8 -0
- data/lib/scout/offsite/ssh.rb +171 -0
- data/lib/scout/offsite/step.rb +83 -0
- data/lib/scout/offsite/sync.rb +55 -0
- data/lib/scout/offsite.rb +3 -0
- data/lib/scout/open/lock.rb +5 -24
- data/lib/scout/open/remote.rb +12 -1
- data/lib/scout/open/stream.rb +110 -122
- data/lib/scout/open/util.rb +9 -0
- data/lib/scout/open.rb +5 -4
- data/lib/scout/path/find.rb +15 -10
- data/lib/scout/path/util.rb +5 -0
- data/lib/scout/persist/serialize.rb +3 -3
- data/lib/scout/persist.rb +1 -1
- data/lib/scout/resource/path.rb +4 -0
- data/lib/scout/resource/util.rb +10 -4
- data/lib/scout/tsv/dumper.rb +2 -0
- data/lib/scout/tsv/index.rb +28 -86
- data/lib/scout/tsv/open.rb +35 -14
- data/lib/scout/tsv/parser.rb +9 -2
- data/lib/scout/tsv/persist/tokyocabinet.rb +2 -0
- data/lib/scout/tsv/stream.rb +204 -0
- data/lib/scout/tsv/transformer.rb +11 -0
- data/lib/scout/tsv.rb +9 -2
- data/lib/scout/work_queue/worker.rb +2 -2
- data/lib/scout/work_queue.rb +36 -12
- data/lib/scout/workflow/definition.rb +2 -1
- data/lib/scout/workflow/deployment/orchestrator.rb +245 -0
- data/lib/scout/workflow/deployment.rb +1 -0
- data/lib/scout/workflow/step/dependencies.rb +37 -11
- data/lib/scout/workflow/step/file.rb +5 -0
- data/lib/scout/workflow/step/info.rb +5 -3
- data/lib/scout/workflow/step/load.rb +1 -1
- data/lib/scout/workflow/step/provenance.rb +1 -0
- data/lib/scout/workflow/step/status.rb +6 -8
- data/lib/scout/workflow/step.rb +75 -30
- data/lib/scout/workflow/task/dependencies.rb +114 -0
- data/lib/scout/workflow/task/inputs.rb +27 -13
- data/lib/scout/workflow/task.rb +9 -108
- data/lib/scout/workflow/usage.rb +40 -12
- data/lib/scout/workflow.rb +4 -2
- data/lib/scout-gear.rb +2 -0
- data/lib/scout.rb +6 -0
- data/scout-gear.gemspec +32 -7
- data/scout_commands/doc +37 -0
- data/scout_commands/find +1 -0
- data/scout_commands/offsite +30 -0
- data/scout_commands/update +29 -0
- data/scout_commands/workflow/info +15 -3
- data/scout_commands/workflow/install +102 -0
- data/scout_commands/workflow/task +26 -5
- data/test/scout/offsite/test_ssh.rb +15 -0
- data/test/scout/offsite/test_step.rb +33 -0
- data/test/scout/offsite/test_sync.rb +36 -0
- data/test/scout/offsite/test_task.rb +0 -0
- data/test/scout/resource/test_path.rb +6 -0
- data/test/scout/test_named_array.rb +6 -0
- data/test/scout/test_persist.rb +3 -2
- data/test/scout/test_tsv.rb +17 -0
- data/test/scout/test_work_queue.rb +63 -41
- data/test/scout/tsv/persist/test_adapter.rb +1 -1
- data/test/scout/tsv/test_index.rb +14 -0
- data/test/scout/tsv/test_parser.rb +14 -0
- data/test/scout/tsv/test_stream.rb +200 -0
- data/test/scout/tsv/test_transformer.rb +12 -0
- data/test/scout/workflow/deployment/test_orchestrator.rb +272 -0
- data/test/scout/workflow/step/test_dependencies.rb +68 -0
- data/test/scout/workflow/step/test_info.rb +18 -0
- data/test/scout/workflow/step/test_status.rb +0 -1
- data/test/scout/workflow/task/test_dependencies.rb +355 -0
- data/test/scout/workflow/task/test_inputs.rb +53 -0
- data/test/scout/workflow/test_definition.rb +18 -0
- data/test/scout/workflow/test_documentation.rb +24 -0
- data/test/scout/workflow/test_step.rb +109 -0
- data/test/scout/workflow/test_task.rb +0 -287
- data/test/test_scout.rb +9 -0
- metadata +83 -5
- data/scout_commands/workflow/task_old +0 -706
data/lib/scout/log.rb
CHANGED
@@ -12,9 +12,9 @@ module Log
|
|
12
12
|
end
|
13
13
|
|
14
14
|
SEVERITY_NAMES ||= begin
|
15
|
-
names = %w(DEBUG LOW MEDIUM HIGH INFO WARN ERROR NONE )
|
15
|
+
names = %w(DEBUG LOW MEDIUM HIGH INFO WARN ERROR NONE )
|
16
16
|
names.each_with_index do |name,i|
|
17
|
-
eval "#{ name } = #{ i }"
|
17
|
+
eval "#{ name } = #{ i }"
|
18
18
|
end
|
19
19
|
names
|
20
20
|
end
|
@@ -32,14 +32,14 @@ module Log
|
|
32
32
|
@@default_severity
|
33
33
|
end
|
34
34
|
|
35
|
-
case ENV['SCOUT_LOG']
|
36
|
-
when 'DEBUG'
|
35
|
+
case ENV['SCOUT_LOG']
|
36
|
+
when 'DEBUG'
|
37
37
|
self.severity = DEBUG
|
38
|
-
when 'LOW'
|
38
|
+
when 'LOW'
|
39
39
|
self.severity = LOW
|
40
|
-
when 'MEDIUM'
|
40
|
+
when 'MEDIUM'
|
41
41
|
self.severity = MEDIUM
|
42
|
-
when 'HIGH'
|
42
|
+
when 'HIGH'
|
43
43
|
self.severity = HIGH
|
44
44
|
when nil
|
45
45
|
self.severity = default_severity
|
@@ -50,23 +50,27 @@ module Log
|
|
50
50
|
|
51
51
|
|
52
52
|
def self.tty_size
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
@@tty_size ||= Log.ignore_stderr do
|
54
|
+
begin
|
55
|
+
IO.console.winsize.last
|
56
|
+
rescue Exception
|
57
|
+
begin
|
58
|
+
res = `tput li`
|
59
|
+
res = nil if res == ""
|
60
|
+
res || ENV["TTY_SIZE"] || 80
|
61
|
+
rescue Exception
|
62
|
+
ENV["TTY_SIZE"] || 80
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
62
66
|
end
|
63
67
|
|
64
68
|
|
65
69
|
def self.last_caller(stack)
|
66
70
|
line = nil
|
67
71
|
pos ||= 0
|
68
|
-
while line.nil? or line =~ /scout\/log\.rb/ and stack.any?
|
69
|
-
line = stack.shift
|
72
|
+
while line.nil? or line =~ /scout\/log\.rb/ and stack.any?
|
73
|
+
line = stack.shift
|
70
74
|
end
|
71
75
|
line ||= caller.first
|
72
76
|
line.gsub('`', "'")
|
@@ -129,9 +133,36 @@ module Log
|
|
129
133
|
out.puts Log.return_line << " " * (Log.tty_size || 80) << Log.return_line unless nocolor
|
130
134
|
end
|
131
135
|
|
136
|
+
MUTEX = Mutex.new
|
137
|
+
def self.log_write(str)
|
138
|
+
MUTEX.synchronize do
|
139
|
+
if logfile.nil?
|
140
|
+
begin
|
141
|
+
STDERR.write str
|
142
|
+
rescue
|
143
|
+
end
|
144
|
+
else
|
145
|
+
logfile.write str
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.log_puts(str)
|
151
|
+
MUTEX.synchronize do
|
152
|
+
if logfile.nil?
|
153
|
+
begin
|
154
|
+
STDERR.puts str
|
155
|
+
rescue
|
156
|
+
end
|
157
|
+
else
|
158
|
+
logfile.puts str
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
132
163
|
LAST = "log"
|
133
164
|
def self.logn(message = nil, severity = MEDIUM, &block)
|
134
|
-
return if severity < self.severity
|
165
|
+
return if severity < self.severity
|
135
166
|
message ||= block.call if block_given?
|
136
167
|
return if message.nil?
|
137
168
|
|
@@ -147,17 +178,14 @@ module Log
|
|
147
178
|
message = "" << highlight << message << color(0) if severity >= INFO
|
148
179
|
str = prefix << " " << message.to_s
|
149
180
|
|
150
|
-
|
151
|
-
|
152
|
-
else
|
153
|
-
logfile.write str
|
154
|
-
end
|
181
|
+
log_write str
|
182
|
+
|
155
183
|
Log::LAST.replace "log"
|
156
184
|
nil
|
157
185
|
end
|
158
186
|
|
159
187
|
def self.log(message = nil, severity = MEDIUM, &block)
|
160
|
-
return if severity < self.severity
|
188
|
+
return if severity < self.severity
|
161
189
|
message ||= block.call if block_given?
|
162
190
|
return if message.nil?
|
163
191
|
message = message + "\n" unless message[-1] == "\n"
|
@@ -250,9 +278,9 @@ module Log
|
|
250
278
|
end
|
251
279
|
|
252
280
|
def self.tsv(tsv, example = false)
|
253
|
-
|
254
|
-
|
255
|
-
|
281
|
+
log_puts Log.color :magenta, "TSV log: " << Log.last_caller(caller).gsub('`',"'")
|
282
|
+
log_puts Log.color(:blue, "=> "<< Log.fingerprint(tsv), true)
|
283
|
+
log_puts Log.color(:cyan, "=> " << tsv.summary)
|
256
284
|
if example && ! tsv.empty?
|
257
285
|
key = case example
|
258
286
|
when TrueClass, :first, "first"
|
@@ -266,11 +294,11 @@ module Log
|
|
266
294
|
values = tsv[key]
|
267
295
|
values = [values] if tsv.type == :flat || tsv.type == :single
|
268
296
|
if values.nil?
|
269
|
-
|
297
|
+
log_puts Log.color(:blue, "Key (#{tsv.key_field}) not present: ") + key
|
270
298
|
else
|
271
|
-
|
299
|
+
log_puts Log.color(:blue, "Key (#{tsv.key_field}): ") + key
|
272
300
|
tsv.fields.zip(values).each do |field,value|
|
273
|
-
|
301
|
+
log_puts Log.color(:magenta, field + ": ") + (Array === value ? value * ", " : value.to_s)
|
274
302
|
end
|
275
303
|
end
|
276
304
|
end
|
@@ -278,14 +306,14 @@ module Log
|
|
278
306
|
|
279
307
|
def self.stack(stack)
|
280
308
|
if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
|
281
|
-
|
309
|
+
log_puts Log.color :magenta, "Stack trace [#{Process.pid}]: " << Log.last_caller(caller)
|
282
310
|
color_stack(stack).each do |line|
|
283
|
-
|
311
|
+
log_puts line
|
284
312
|
end
|
285
313
|
else
|
286
|
-
|
314
|
+
log_puts Log.color :magenta, "Stack trace [#{Process.pid}]: " << Log.last_caller(caller)
|
287
315
|
color_stack(stack.reverse).each do |line|
|
288
|
-
|
316
|
+
log_puts line
|
289
317
|
end
|
290
318
|
end
|
291
319
|
end
|
@@ -293,7 +321,7 @@ module Log
|
|
293
321
|
def self.count_stack
|
294
322
|
if ! $count_stacks
|
295
323
|
Log.debug "Counting stacks at: " << caller.first
|
296
|
-
return
|
324
|
+
return
|
297
325
|
end
|
298
326
|
$stack_counts ||= {}
|
299
327
|
head = $count_stacks_head
|
data/lib/scout/meta_extension.rb
CHANGED
@@ -62,5 +62,39 @@ module MetaExtension
|
|
62
62
|
end
|
63
63
|
base.setup(other, *attr_values)
|
64
64
|
end
|
65
|
+
|
66
|
+
base.define_method(:purge) do
|
67
|
+
new = self.dup
|
68
|
+
|
69
|
+
if new.instance_variables.include?(:@extension_attrs)
|
70
|
+
new.instance_variable_get(:@extension_attrs).each do |a|
|
71
|
+
new.remove_instance_variable("@#{a}")
|
72
|
+
end
|
73
|
+
new.remove_instance_variable("@extension_attrs")
|
74
|
+
end
|
75
|
+
|
76
|
+
new
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.is_extended?(obj)
|
81
|
+
obj.respond_to?(:extension_attr_hash)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.purge(obj)
|
85
|
+
case obj
|
86
|
+
when nil
|
87
|
+
nil
|
88
|
+
when Array
|
89
|
+
obj.collect{|e| purge(e) }
|
90
|
+
when Hash
|
91
|
+
new = {}
|
92
|
+
obj.each do |k,v|
|
93
|
+
new[k] = purge(v)
|
94
|
+
end
|
95
|
+
new
|
96
|
+
else
|
97
|
+
is_extended?(obj) ? obj.purge : obj
|
98
|
+
end
|
65
99
|
end
|
66
100
|
end
|
data/lib/scout/misc/digest.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Misc
|
2
|
+
MAX_ARRAY_DIGEST_LENGTH = 100_000
|
2
3
|
def self.digest_str(obj)
|
3
4
|
if obj.respond_to?(:digest_str)
|
4
5
|
obj.digest_str
|
@@ -9,12 +10,19 @@ module Misc
|
|
9
10
|
if Path === obj || ! Open.exists?(obj)
|
10
11
|
'\'' << obj << '\''
|
11
12
|
else
|
12
|
-
Misc.file_md5(obj)
|
13
|
+
"File MD5: #{Misc.file_md5(obj)}"
|
13
14
|
end
|
14
15
|
when Integer, Symbol
|
15
16
|
obj.to_s
|
16
17
|
when Array
|
17
|
-
|
18
|
+
if obj.length > MAX_ARRAY_DIGEST_LENGTH
|
19
|
+
length = obj.length
|
20
|
+
mid = length/2
|
21
|
+
sample_pos = [1, 2, mid, length-2, length-1]
|
22
|
+
"[#{length}:" << obj.values_at(*sample_pos).inject(""){|acc,o| acc.empty? ? Misc.digest_str(o) : acc << ', ' << Misc.digest_str(o) } << ']'
|
23
|
+
else
|
24
|
+
'[' << obj.inject(""){|acc,o| acc.empty? ? Misc.digest_str(o) : acc << ', ' << Misc.digest_str(o) } << ']'
|
25
|
+
end
|
18
26
|
when Hash
|
19
27
|
'{' << obj.inject(""){|acc,p| s = Misc.digest_str(p.first) << "=" << Misc.digest_str(p.last); acc.empty? ? s : acc << ', ' << s } << '}'
|
20
28
|
when Integer
|
@@ -46,6 +54,7 @@ module Misc
|
|
46
54
|
|
47
55
|
def self.file_md5(file)
|
48
56
|
file = file.find if Path === file
|
57
|
+
file = File.expand_path(file)
|
49
58
|
#md5file = file + '.md5'
|
50
59
|
Persist.persist("MD5:#{file}", :string) do
|
51
60
|
Digest::MD5.file(file).hexdigest
|
data/lib/scout/misc/format.rb
CHANGED
@@ -38,10 +38,10 @@ module Misc
|
|
38
38
|
|
39
39
|
|
40
40
|
|
41
|
-
|
41
|
+
MAX_TTY_LINE_WIDTH = 100
|
42
42
|
def self.format_paragraph(text, size = nil, indent = nil, offset = nil)
|
43
|
-
size ||= Log.tty_size ||
|
44
|
-
size =
|
43
|
+
size ||= Log.tty_size || MAX_TTY_LINE_WIDTH
|
44
|
+
size = MAX_TTY_LINE_WIDTH if size > MAX_TTY_LINE_WIDTH
|
45
45
|
indent ||= 0
|
46
46
|
offset ||= 0
|
47
47
|
|
@@ -75,9 +75,14 @@ module Misc
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def self.format_definition_list_item(dt, dd, indent = nil, size = nil, color = :yellow)
|
78
|
-
size
|
79
|
-
|
80
|
-
|
78
|
+
if size.nil?
|
79
|
+
base_size = MAX_TTY_LINE_WIDTH
|
80
|
+
base_indent = indent || (base_size / 3)
|
81
|
+
size = base_size - base_indent
|
82
|
+
end
|
83
|
+
|
84
|
+
indent ||= base_indent || size / 3
|
85
|
+
|
81
86
|
dd = "" if dd.nil?
|
82
87
|
dt = Log.color color, dt if color
|
83
88
|
dt = dt.to_s unless dd.empty?
|
@@ -96,8 +101,8 @@ module Misc
|
|
96
101
|
end
|
97
102
|
|
98
103
|
def self.format_definition_list(defs, indent = nil, size = nil, color = :yellow, sep = "\n\n")
|
99
|
-
size ||= Log.tty_size || MAX_WIDTH
|
100
104
|
indent ||= 30
|
105
|
+
size ||= (Log.tty_size || MAX_TTY_LINE_WIDTH) - indent
|
101
106
|
entries = []
|
102
107
|
defs.each do |dt,dd|
|
103
108
|
text = format_definition_list_item(dt,dd,indent, size,color)
|
data/lib/scout/misc/monitor.rb
CHANGED
@@ -2,6 +2,7 @@ module Misc
|
|
2
2
|
def self.pid_alive?(pid)
|
3
3
|
!! Process.kill(0, pid) rescue false
|
4
4
|
end
|
5
|
+
|
5
6
|
def self.benchmark(repeats = 1, message = nil)
|
6
7
|
require 'benchmark'
|
7
8
|
res = nil
|
@@ -52,4 +53,14 @@ module Misc
|
|
52
53
|
end
|
53
54
|
eend - start
|
54
55
|
end
|
56
|
+
|
57
|
+
def self.wait_for_interrupt
|
58
|
+
while true
|
59
|
+
begin
|
60
|
+
sleep 1
|
61
|
+
rescue Interrupt
|
62
|
+
break
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
55
66
|
end
|
data/lib/scout/misc/system.rb
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
+
require 'sys/proctable'
|
2
|
+
|
1
3
|
module Misc
|
4
|
+
|
5
|
+
def self.hostname
|
6
|
+
@@hostname ||= begin
|
7
|
+
`hostname`.strip
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.children(ppid = nil)
|
12
|
+
ppid ||= Process.pid
|
13
|
+
Sys::ProcTable.ps.select{ |pe| pe.ppid == ppid }
|
14
|
+
end
|
15
|
+
|
2
16
|
def self.env_add(var, value, sep = ":", prepend = true)
|
3
17
|
if ENV[var].nil?
|
4
18
|
ENV[var] = value
|
@@ -22,4 +36,38 @@ module Misc
|
|
22
36
|
ENV[var] = old_value
|
23
37
|
end
|
24
38
|
end
|
39
|
+
|
40
|
+
def self.update_git(gem_name = 'scout-gear')
|
41
|
+
gem_name = 'scout-gear' if gem_name.nil?
|
42
|
+
dir = File.join(__dir__, '../../../../', gem_name)
|
43
|
+
return unless Open.exist?(dir)
|
44
|
+
Misc.in_dir dir do
|
45
|
+
begin
|
46
|
+
begin
|
47
|
+
CMD.cmd_log('git pull')
|
48
|
+
rescue
|
49
|
+
raise "Could not update #{gem_name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
CMD.cmd_log('git submodule update')
|
54
|
+
rescue
|
55
|
+
raise "Could not update #{gem_name} submodules"
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
begin
|
60
|
+
CMD.cmd_log('rake install')
|
61
|
+
rescue
|
62
|
+
raise "Could not install updated #{gem_name}"
|
63
|
+
end
|
64
|
+
rescue
|
65
|
+
Log.warn $!.message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.processors
|
71
|
+
Etc.nprocessors
|
72
|
+
end
|
25
73
|
end
|
data/lib/scout/named_array.rb
CHANGED
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
class SSHLine
|
4
|
+
class << self
|
5
|
+
attr_accessor :default_server
|
6
|
+
def default_server
|
7
|
+
@@default_server ||= begin
|
8
|
+
ENV["SCOUT_OFFSITE"] || ENV["SCOUT_SERVER"] || 'localhost'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(host = :default, user = nil)
|
14
|
+
host = SSHLine.default_server if host.nil? || host == :default
|
15
|
+
@host = host
|
16
|
+
@user = user
|
17
|
+
|
18
|
+
@ssh = Net::SSH.start(@host, @user)
|
19
|
+
|
20
|
+
@ch = @ssh.open_channel do |ch|
|
21
|
+
ch.exec 'bash -l'
|
22
|
+
end
|
23
|
+
|
24
|
+
@ch.on_data do |_,data|
|
25
|
+
if m = data.match(/DONECMD: (\d+)\n/)
|
26
|
+
@exit_status = m[1].to_i
|
27
|
+
@output << data.sub(m[0],'')
|
28
|
+
serve_output
|
29
|
+
else
|
30
|
+
@output << data
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@ch.on_extended_data do |_,c,err|
|
35
|
+
STDERR.write err
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def self.reach?(server = SSHLine.default_server)
|
41
|
+
Persist.memory(server, :key => "Reach server") do
|
42
|
+
begin
|
43
|
+
CMD.cmd("ssh #{server} bash -l -c \"scout\"")
|
44
|
+
true
|
45
|
+
rescue Exception
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_cmd(command)
|
52
|
+
@output = ""
|
53
|
+
@complete_output = false
|
54
|
+
@ch.send_data(command+"\necho DONECMD: $?\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
def serve_output
|
58
|
+
@complete_output = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def run(command)
|
62
|
+
send_cmd(command)
|
63
|
+
@ssh.loop{ ! @complete_output}
|
64
|
+
if @exit_status.to_i == 0
|
65
|
+
return @output
|
66
|
+
else
|
67
|
+
raise SSHProcessFailed.new @host, command
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def ruby(script)
|
72
|
+
@output = ""
|
73
|
+
@complete_output = false
|
74
|
+
cmd = "ruby -e \"#{script.gsub('"','\\"')}\"\n"
|
75
|
+
Log.debug "Running ruby on #{@host}:\n#{ script }"
|
76
|
+
@ch.send_data(cmd)
|
77
|
+
@ch.send_data("echo DONECMD: $?\n")
|
78
|
+
@ssh.loop{ !@complete_output }
|
79
|
+
if @exit_status.to_i == 0
|
80
|
+
return @output
|
81
|
+
else
|
82
|
+
raise SSHProcessFailed.new @host, "Ruby script:\n#{script}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def scout(script)
|
87
|
+
scout_script =<<-EOF
|
88
|
+
require 'scout'
|
89
|
+
SSHLine.run_local do
|
90
|
+
#{script.strip}
|
91
|
+
end
|
92
|
+
EOF
|
93
|
+
|
94
|
+
m = ruby(scout_script)
|
95
|
+
Marshal.load m
|
96
|
+
end
|
97
|
+
|
98
|
+
def workflow(workflow, script)
|
99
|
+
preamble =<<-EOF
|
100
|
+
wf = Workflow.require_workflow('#{workflow}')
|
101
|
+
EOF
|
102
|
+
|
103
|
+
scout(preamble + "\n" + script)
|
104
|
+
end
|
105
|
+
|
106
|
+
class Mock < SSHLine
|
107
|
+
def initialize
|
108
|
+
end
|
109
|
+
|
110
|
+
def run(command)
|
111
|
+
CMD.cmd(command)
|
112
|
+
end
|
113
|
+
|
114
|
+
def ruby(script)
|
115
|
+
cmd = "ruby -e \"#{script.gsub('"','\\"')}\"\n"
|
116
|
+
CMD.cmd(cmd)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
@connections = {}
|
121
|
+
def self.open(host, user = nil)
|
122
|
+
@connections[[host, user]] ||=
|
123
|
+
begin
|
124
|
+
if host == 'localhost'
|
125
|
+
SSHLine::Mock.new
|
126
|
+
else
|
127
|
+
SSHLine.new host, user
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.run(server, cmd, options = nil)
|
133
|
+
cmd = cmd * " " if Array === cmd
|
134
|
+
cmd += " " + CMD.process_cmd_options(options) if options
|
135
|
+
open(server).run(cmd)
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.ruby(server, script)
|
139
|
+
open(server).ruby(script)
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.scout(server, script)
|
143
|
+
open(server).scout(script)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.workflow(server, workflow, script)
|
147
|
+
open(server).workflow(workflow, script)
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.command(server, command, argv = [], options = nil)
|
151
|
+
command = "scout #{command}" unless command && command.include?('scout')
|
152
|
+
argv_str = (argv - ["--"]).collect{|v| '"' + v.to_s + '"' } * " "
|
153
|
+
command = "#{command} #{argv_str}"
|
154
|
+
Log.debug "Offsite #{server} running: #{command}"
|
155
|
+
run(server, command, options)
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.mkdir(server, path)
|
159
|
+
self.run server, "mkdir -p '#{path}'"
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.run_local(&block)
|
163
|
+
res = begin
|
164
|
+
old_stdout = STDOUT.dup; STDOUT.reopen(STDERR)
|
165
|
+
block.call
|
166
|
+
ensure
|
167
|
+
STDOUT.reopen(old_stdout)
|
168
|
+
end
|
169
|
+
puts Marshal.dump(res)
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative '../workflow/step'
|
2
|
+
require_relative 'ssh'
|
3
|
+
require_relative 'sync'
|
4
|
+
|
5
|
+
module OffsiteStep
|
6
|
+
|
7
|
+
extend MetaExtension
|
8
|
+
extension_attr :server, :workflow_name, :clean_id, :provided_inputs
|
9
|
+
|
10
|
+
def inputs_directory
|
11
|
+
@inputs_directory ||= begin
|
12
|
+
if provided_inputs && provided_inputs.any?
|
13
|
+
file = ".scout/tmp/step_inputs/#{workflow}/#{task_name}/#{name}"
|
14
|
+
TmpFile.with_path do |inputs_dir|
|
15
|
+
task.save_inputs(inputs_dir, provided_inputs)
|
16
|
+
SSHLine.rsync(inputs_dir, file, target: server, directory: true)
|
17
|
+
end
|
18
|
+
file
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def workflow_name
|
24
|
+
@workflow_name || workflow.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def offsite_job_ssh(script)
|
28
|
+
parts = []
|
29
|
+
parts << <<~EOF.strip
|
30
|
+
wf = Workflow.require_workflow "#{workflow_name}";
|
31
|
+
EOF
|
32
|
+
|
33
|
+
if inputs_directory
|
34
|
+
parts << <<~EOF.strip
|
35
|
+
job = wf.job(:#{task_name}, "#{clean_name}", :load_inputs => "#{inputs_directory}");
|
36
|
+
EOF
|
37
|
+
else
|
38
|
+
parts << <<~EOF.strip
|
39
|
+
job = wf.job(:#{task_name}, "#{clean_name}");
|
40
|
+
EOF
|
41
|
+
end
|
42
|
+
|
43
|
+
parts << script
|
44
|
+
|
45
|
+
|
46
|
+
SSHLine.scout server, parts * "\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
def offsite_path
|
50
|
+
@path = offsite_job_ssh <<~EOF
|
51
|
+
job.path.identify
|
52
|
+
EOF
|
53
|
+
end
|
54
|
+
|
55
|
+
def info
|
56
|
+
info = @info ||= offsite_job_ssh <<~EOF
|
57
|
+
info = Open.exists?(job.info_file) ? job.info : {}
|
58
|
+
info[:running] = true if job.running?
|
59
|
+
info
|
60
|
+
EOF
|
61
|
+
|
62
|
+
@info = nil unless %w(done aborted error).include?(info[:status].to_s)
|
63
|
+
|
64
|
+
info
|
65
|
+
end
|
66
|
+
|
67
|
+
def done?
|
68
|
+
status == :done
|
69
|
+
end
|
70
|
+
|
71
|
+
def exec
|
72
|
+
bundle_files = offsite_job_ssh <<~EOF
|
73
|
+
job.run
|
74
|
+
job.bundle_files
|
75
|
+
EOF
|
76
|
+
SSHLine.sync(bundle_files, source: server)
|
77
|
+
self.load
|
78
|
+
end
|
79
|
+
|
80
|
+
def run
|
81
|
+
exec
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class SSHLine
|
2
|
+
def self.locate(server, paths, map: :user)
|
3
|
+
SSHLine.scout server, <<-EOF
|
4
|
+
map = :#{map}
|
5
|
+
paths = [#{paths.collect{|p| "'" + p + "'" } * ", " }]
|
6
|
+
located = paths.collect{|p| Path.setup(p).find(map) }
|
7
|
+
identified = paths.collect{|p| Resource.identify(p) }
|
8
|
+
[located, identified]
|
9
|
+
EOF
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.rsync(source_path, target_path, directory: false, source: nil, target: nil, dry_run: false, hard_link: false)
|
13
|
+
rsync_args = "-avztHP --copy-unsafe-links --omit-dir-times "
|
14
|
+
|
15
|
+
rsync_args << "--link-dest '#{source_path}' " if hard_link && ! source
|
16
|
+
|
17
|
+
source_path = source_path + "/" if directory && ! source_path.end_with?("/")
|
18
|
+
target_path = target_path + "/" if directory && ! target_path.end_with?("/")
|
19
|
+
if target
|
20
|
+
SSHLine.mkdir target, File.dirname(target_path)
|
21
|
+
else
|
22
|
+
Open.mkdir(File.dirname(target_path))
|
23
|
+
end
|
24
|
+
|
25
|
+
cmd = 'rsync '
|
26
|
+
cmd << rsync_args
|
27
|
+
cmd << '-nv ' if dry_run
|
28
|
+
cmd << (source ? [source, source_path] * ":" : source_path) << " "
|
29
|
+
cmd << (target ? [target, target_path] * ":" : target_path) << " "
|
30
|
+
|
31
|
+
CMD.cmd_log(cmd, :log => Log::HIGH)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.sync(paths, source: nil, target: nil, map: :user, **kwargs)
|
35
|
+
source = nil if source == 'localhost'
|
36
|
+
target = nil if target == 'localhost'
|
37
|
+
|
38
|
+
if source
|
39
|
+
source_paths, identified_paths = SSHLine.locate(source, paths)
|
40
|
+
else
|
41
|
+
source_paths = paths.collect{|p| Path === p ? p.find : p }
|
42
|
+
identified_paths = paths.collect{|p| Resource.identify(p) }
|
43
|
+
end
|
44
|
+
|
45
|
+
if target
|
46
|
+
target_paths = SSHLine.locate(target, identified_paths, map: map)
|
47
|
+
else
|
48
|
+
target_paths = identified_paths.collect{|p| p.find(map) }
|
49
|
+
end
|
50
|
+
|
51
|
+
source_paths.zip(target_paths).each do |source_path,target_path|
|
52
|
+
rsync(source_path, target_path, source: source, target: target, **kwargs)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|