scout-gear 7.3.0 → 8.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.vimproject +44 -16
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/scout +21 -7
- data/doc/lib/scout/path.md +35 -0
- data/doc/lib/scout/workflow/task.md +13 -0
- data/lib/rbbt-scout.rb +1 -0
- data/lib/scout/cmd.rb +24 -25
- data/lib/scout/concurrent_stream.rb +59 -39
- data/lib/scout/config.rb +1 -1
- data/lib/scout/exceptions.rb +10 -0
- data/lib/scout/log/color.rb +15 -12
- data/lib/scout/log/progress/report.rb +8 -6
- data/lib/scout/log/progress/util.rb +61 -54
- data/lib/scout/log/progress.rb +1 -1
- data/lib/scout/log/trap.rb +107 -0
- data/lib/scout/log.rb +115 -52
- data/lib/scout/meta_extension.rb +47 -6
- data/lib/scout/misc/digest.rb +12 -3
- data/lib/scout/misc/format.rb +24 -7
- data/lib/scout/misc/insist.rb +1 -1
- data/lib/scout/misc/monitor.rb +22 -0
- data/lib/scout/misc/system.rb +58 -0
- data/lib/scout/named_array.rb +73 -3
- 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/lockfile.rb +587 -0
- data/lib/scout/open/lock.rb +9 -2
- data/lib/scout/open/remote.rb +16 -1
- data/lib/scout/open/stream.rb +146 -83
- data/lib/scout/open/util.rb +22 -3
- data/lib/scout/open.rb +5 -4
- data/lib/scout/path/find.rb +24 -11
- data/lib/scout/path/util.rb +40 -0
- data/lib/scout/persist/serialize.rb +19 -6
- data/lib/scout/persist.rb +29 -13
- data/lib/scout/resource/path.rb +57 -0
- data/lib/scout/resource/produce.rb +0 -8
- data/lib/scout/resource/util.rb +12 -5
- data/lib/scout/tmpfile.rb +7 -8
- data/lib/scout/tsv/attach.rb +177 -0
- data/lib/scout/tsv/change_id.rb +40 -0
- data/lib/scout/tsv/dumper.rb +74 -46
- data/lib/scout/tsv/index.rb +85 -87
- data/lib/scout/tsv/open.rb +160 -85
- data/lib/scout/tsv/parser.rb +142 -80
- data/lib/scout/tsv/path.rb +1 -2
- data/lib/scout/tsv/persist/adapter.rb +15 -45
- data/lib/scout/tsv/persist/fix_width_table.rb +3 -0
- data/lib/scout/tsv/persist/tokyocabinet.rb +6 -1
- data/lib/scout/tsv/persist.rb +4 -0
- data/lib/scout/tsv/stream.rb +204 -0
- data/lib/scout/tsv/transformer.rb +152 -0
- data/lib/scout/tsv/traverse.rb +96 -92
- data/lib/scout/tsv/util/filter.rb +9 -0
- data/lib/scout/tsv/util/reorder.rb +81 -0
- data/lib/scout/tsv/util/select.rb +78 -33
- data/lib/scout/tsv/util/unzip.rb +86 -0
- data/lib/scout/tsv/util.rb +60 -11
- data/lib/scout/tsv.rb +34 -4
- data/lib/scout/work_queue/socket.rb +6 -1
- data/lib/scout/work_queue/worker.rb +5 -2
- data/lib/scout/work_queue.rb +51 -20
- data/lib/scout/workflow/definition.rb +23 -3
- data/lib/scout/workflow/deployment/orchestrator.rb +245 -0
- data/lib/scout/workflow/deployment.rb +1 -0
- data/lib/scout/workflow/step/dependencies.rb +56 -10
- data/lib/scout/workflow/step/file.rb +5 -0
- data/lib/scout/workflow/step/info.rb +40 -7
- data/lib/scout/workflow/step/load.rb +1 -1
- data/lib/scout/workflow/step/provenance.rb +9 -7
- data/lib/scout/workflow/step/status.rb +43 -0
- data/lib/scout/workflow/step.rb +160 -49
- data/lib/scout/workflow/task/dependencies.rb +114 -0
- data/lib/scout/workflow/task/inputs.rb +40 -32
- data/lib/scout/workflow/task.rb +38 -102
- data/lib/scout/workflow/usage.rb +48 -18
- data/lib/scout/workflow.rb +4 -2
- data/lib/scout-gear.rb +2 -0
- data/lib/scout.rb +6 -0
- data/scout-gear.gemspec +52 -23
- 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 +57 -9
- 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/open/test_stream.rb +60 -58
- data/test/scout/path/test_find.rb +10 -1
- data/test/scout/resource/test_path.rb +6 -0
- data/test/scout/resource/test_produce.rb +15 -0
- data/test/scout/test_meta_extension.rb +25 -0
- data/test/scout/test_named_array.rb +24 -0
- data/test/scout/test_persist.rb +9 -2
- data/test/scout/test_tsv.rb +229 -2
- data/test/scout/test_work_queue.rb +65 -41
- data/test/scout/tsv/persist/test_tokyocabinet.rb +29 -1
- data/test/scout/tsv/test_attach.rb +227 -0
- data/test/scout/tsv/test_change_id.rb +98 -0
- data/test/scout/tsv/test_dumper.rb +1 -1
- data/test/scout/tsv/test_index.rb +49 -3
- data/test/scout/tsv/test_open.rb +160 -2
- data/test/scout/tsv/test_parser.rb +33 -2
- data/test/scout/tsv/test_persist.rb +2 -0
- data/test/scout/tsv/test_stream.rb +200 -0
- data/test/scout/tsv/test_transformer.rb +120 -0
- data/test/scout/tsv/test_traverse.rb +88 -3
- data/test/scout/tsv/test_util.rb +1 -0
- data/test/scout/tsv/util/test_reorder.rb +94 -0
- data/test/scout/tsv/util/test_select.rb +25 -11
- data/test/scout/tsv/util/test_unzip.rb +112 -0
- data/test/scout/work_queue/test_socket.rb +0 -1
- 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 +30 -0
- data/test/scout/workflow/task/test_dependencies.rb +355 -0
- data/test/scout/workflow/task/test_inputs.rb +67 -14
- 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 +112 -3
- data/test/scout/workflow/test_task.rb +0 -151
- data/test/scout/workflow/test_usage.rb +33 -6
- data/test/test_scout.rb +9 -0
- metadata +100 -8
- data/scout_commands/workflow/task_old +0 -706
data/lib/scout/meta_extension.rb
CHANGED
@@ -2,7 +2,7 @@ module MetaExtension
|
|
2
2
|
def self.extended(base)
|
3
3
|
meta = class << base; self; end
|
4
4
|
|
5
|
-
base.class_variable_set("@@extension_attrs", [])
|
5
|
+
base.class_variable_set("@@extension_attrs", []) unless base.class_variables.include?("@@extension_attrs")
|
6
6
|
|
7
7
|
meta.define_method(:extension_attr) do |*attrs|
|
8
8
|
self.class_variable_get("@@extension_attrs").concat attrs
|
@@ -11,6 +11,14 @@ module MetaExtension
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
meta.define_method(:extended) do |obj|
|
15
|
+
attrs = self.class_variable_get("@@extension_attrs")
|
16
|
+
|
17
|
+
obj.instance_variable_set(:@extension_attrs, []) unless obj.instance_variables.include?(:@extension_attrs)
|
18
|
+
extension_attrs = obj.instance_variable_get(:@extension_attrs)
|
19
|
+
extension_attrs.concat attrs
|
20
|
+
end
|
21
|
+
|
14
22
|
meta.define_method(:setup) do |*args,&block|
|
15
23
|
if block_given?
|
16
24
|
obj, rest = block, args
|
@@ -18,7 +26,8 @@ module MetaExtension
|
|
18
26
|
obj, *rest = args
|
19
27
|
end
|
20
28
|
obj = block if obj.nil?
|
21
|
-
obj.extend base
|
29
|
+
obj.extend base unless base === obj
|
30
|
+
|
22
31
|
attrs = self.class_variable_get("@@extension_attrs")
|
23
32
|
|
24
33
|
return if attrs.nil? || attrs.empty?
|
@@ -27,8 +36,6 @@ module MetaExtension
|
|
27
36
|
((! (rlkey = rlast.keys.first).nil? && attrs.include?(rlkey.to_sym)) ||
|
28
37
|
(! attrs.length != 1 ))
|
29
38
|
|
30
|
-
|
31
|
-
|
32
39
|
pairs = rlast
|
33
40
|
else
|
34
41
|
pairs = attrs.zip(rest)
|
@@ -43,17 +50,51 @@ module MetaExtension
|
|
43
50
|
|
44
51
|
base.define_method(:extension_attr_hash) do
|
45
52
|
attr_hash = {}
|
46
|
-
|
53
|
+
@extension_attrs.each do |name|
|
47
54
|
attr_hash[name] = self.instance_variable_get("@#{name}")
|
48
55
|
end
|
49
56
|
attr_hash
|
50
57
|
end
|
51
58
|
|
52
59
|
base.define_method(:annotate) do |other|
|
53
|
-
attr_values =
|
60
|
+
attr_values = @extension_attrs.collect do |a|
|
54
61
|
self.instance_variable_get("@#{a}")
|
55
62
|
end
|
56
63
|
base.setup(other, *attr_values)
|
57
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
|
58
99
|
end
|
59
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
|
@@ -40,12 +48,13 @@ module Misc
|
|
40
48
|
end
|
41
49
|
|
42
50
|
def self.digest(obj)
|
43
|
-
str = Misc.digest_str(obj)
|
51
|
+
str = String === obj ? obj : Misc.digest_str(obj)
|
44
52
|
Digest::MD5.hexdigest(str)
|
45
53
|
end
|
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
@@ -25,11 +25,23 @@ module Misc
|
|
25
25
|
str
|
26
26
|
end
|
27
27
|
|
28
|
+
CHAR_SENCONDS = ENV["SCOUT_NOCOLOR"] == "true" ? "sec" : "″"
|
29
|
+
def self.format_seconds_short(time)
|
30
|
+
if time < 0.0001
|
31
|
+
"%.5g" % time + CHAR_SENCONDS
|
32
|
+
elsif time < 60
|
33
|
+
"%.2g" % time + CHAR_SENCONDS
|
34
|
+
else
|
35
|
+
format_seconds(time)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
28
40
|
|
29
|
-
|
41
|
+
MAX_TTY_LINE_WIDTH = 100
|
30
42
|
def self.format_paragraph(text, size = nil, indent = nil, offset = nil)
|
31
|
-
size ||= Log.tty_size ||
|
32
|
-
size =
|
43
|
+
size ||= Log.tty_size || MAX_TTY_LINE_WIDTH
|
44
|
+
size = MAX_TTY_LINE_WIDTH if size > MAX_TTY_LINE_WIDTH
|
33
45
|
indent ||= 0
|
34
46
|
offset ||= 0
|
35
47
|
|
@@ -63,9 +75,14 @@ module Misc
|
|
63
75
|
end
|
64
76
|
|
65
77
|
def self.format_definition_list_item(dt, dd, indent = nil, size = nil, color = :yellow)
|
66
|
-
size
|
67
|
-
|
68
|
-
|
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
|
+
|
69
86
|
dd = "" if dd.nil?
|
70
87
|
dt = Log.color color, dt if color
|
71
88
|
dt = dt.to_s unless dd.empty?
|
@@ -84,8 +101,8 @@ module Misc
|
|
84
101
|
end
|
85
102
|
|
86
103
|
def self.format_definition_list(defs, indent = nil, size = nil, color = :yellow, sep = "\n\n")
|
87
|
-
size ||= Log.tty_size || MAX_WIDTH
|
88
104
|
indent ||= 30
|
105
|
+
size ||= (Log.tty_size || MAX_TTY_LINE_WIDTH) - indent
|
89
106
|
entries = []
|
90
107
|
defs.each do |dt,dd|
|
91
108
|
text = format_definition_list_item(dt,dd,indent, size,color)
|
data/lib/scout/misc/insist.rb
CHANGED
@@ -32,7 +32,7 @@ module Misc
|
|
32
32
|
end
|
33
33
|
raise $!
|
34
34
|
rescue Exception
|
35
|
-
Log.exception $! if ENV["
|
35
|
+
Log.exception $! if ENV["SCOUT_LOG_INSIST"] == 'true'
|
36
36
|
if msg
|
37
37
|
Log.warn("Insisting after exception: #{$!.class} #{$!.message} -- #{msg}")
|
38
38
|
elsif FalseClass === msg
|
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
|
@@ -41,4 +42,25 @@ module Misc
|
|
41
42
|
|
42
43
|
res
|
43
44
|
end
|
45
|
+
|
46
|
+
def self.exec_time(&block)
|
47
|
+
start = Time.now
|
48
|
+
eend = nil
|
49
|
+
begin
|
50
|
+
yield
|
51
|
+
ensure
|
52
|
+
eend = Time.now
|
53
|
+
end
|
54
|
+
eend - start
|
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
|
44
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
|
@@ -12,4 +26,48 @@ module Misc
|
|
12
26
|
end
|
13
27
|
end
|
14
28
|
end
|
29
|
+
|
30
|
+
def self.with_env(var, value, &block)
|
31
|
+
old_value = ENV[var]
|
32
|
+
begin
|
33
|
+
ENV[var] = value
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
ENV[var] = old_value
|
37
|
+
end
|
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
|
15
73
|
end
|
data/lib/scout/named_array.rb
CHANGED
@@ -1,13 +1,25 @@
|
|
1
1
|
require_relative 'meta_extension'
|
2
2
|
module NamedArray
|
3
3
|
extend MetaExtension
|
4
|
-
extension_attr :fields
|
4
|
+
extension_attr :fields, :key
|
5
5
|
|
6
|
-
def self.
|
6
|
+
def self.field_match(field, name)
|
7
|
+
if (String === field) && (String === name)
|
8
|
+
field == name ||
|
9
|
+
field.start_with?(name) || field.include?("(" + name + ")") ||
|
10
|
+
name.start_with?(field) || name.include?("(" + field + ")")
|
11
|
+
else
|
12
|
+
field == name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.identify_name(names, selected, strict: false)
|
7
17
|
res = (Array === selected ? selected : [selected]).collect do |field|
|
8
18
|
case field
|
9
19
|
when nil
|
10
20
|
0
|
21
|
+
when Range
|
22
|
+
field
|
11
23
|
when Integer
|
12
24
|
field
|
13
25
|
when Symbol
|
@@ -26,7 +38,8 @@ module NamedArray
|
|
26
38
|
if field =~ /^\d+$/
|
27
39
|
next identify_names(names, field.to_i)
|
28
40
|
end
|
29
|
-
pos
|
41
|
+
next pos if strict
|
42
|
+
pos = names.index{|name| field_match(field, name) }
|
30
43
|
next pos if pos
|
31
44
|
nil
|
32
45
|
else
|
@@ -37,6 +50,10 @@ module NamedArray
|
|
37
50
|
Array === selected ? res : res.first
|
38
51
|
end
|
39
52
|
|
53
|
+
def identify_name(selected)
|
54
|
+
NamedArray.identify_name(fields, selected)
|
55
|
+
end
|
56
|
+
|
40
57
|
def positions(fields)
|
41
58
|
if Array == fields
|
42
59
|
fields.collect{|field|
|
@@ -49,6 +66,7 @@ module NamedArray
|
|
49
66
|
|
50
67
|
def [](key)
|
51
68
|
pos = NamedArray.identify_name(@fields, key)
|
69
|
+
return nil if pos.nil?
|
52
70
|
super(pos)
|
53
71
|
end
|
54
72
|
|
@@ -65,4 +83,56 @@ module NamedArray
|
|
65
83
|
end
|
66
84
|
IndiferentHash.setup hash
|
67
85
|
end
|
86
|
+
|
87
|
+
def values_at(*positions)
|
88
|
+
super(*identify_name(positions))
|
89
|
+
end
|
90
|
+
|
91
|
+
def self._zip_fields(array, max = nil)
|
92
|
+
return [] if array.nil? or array.empty? or (first = array.first).nil?
|
93
|
+
|
94
|
+
max = array.collect{|l| l.length }.max if max.nil?
|
95
|
+
|
96
|
+
rest = array[1..-1].collect{|v|
|
97
|
+
v.length == 1 & max > 1 ? v * max : v
|
98
|
+
}
|
99
|
+
|
100
|
+
first = first * max if first.length == 1 and max > 1
|
101
|
+
|
102
|
+
first.zip(*rest)
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.zip_fields(array)
|
106
|
+
if array.length < 10000
|
107
|
+
_zip_fields(array)
|
108
|
+
else
|
109
|
+
zipped_slices = []
|
110
|
+
max = array.collect{|l| l.length}.max
|
111
|
+
array.each_slice(10000) do |slice|
|
112
|
+
zipped_slices << _zip_fields(slice, max)
|
113
|
+
end
|
114
|
+
new = zipped_slices.first
|
115
|
+
zipped_slices[1..-1].each do |rest|
|
116
|
+
rest.each_with_index do |list,i|
|
117
|
+
new[i].concat list
|
118
|
+
end
|
119
|
+
end
|
120
|
+
new
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.add_zipped(source, new)
|
125
|
+
source.zip(new).each do |s,n|
|
126
|
+
s.concat(n)
|
127
|
+
end
|
128
|
+
source
|
129
|
+
end
|
130
|
+
|
131
|
+
def method_missing(name, *args)
|
132
|
+
if identify_name(name)
|
133
|
+
return self[name]
|
134
|
+
else
|
135
|
+
return super(name, *args)
|
136
|
+
end
|
137
|
+
end
|
68
138
|
end
|
@@ -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
|