scout-gear 8.1.0 → 9.0.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +22 -0
  3. data/VERSION +1 -1
  4. data/bin/scout +1 -0
  5. data/lib/rbbt-scout.rb +2 -1
  6. data/lib/scout/cmd.rb +9 -5
  7. data/lib/scout/indiferent_hash.rb +17 -0
  8. data/lib/scout/log/progress/report.rb +1 -0
  9. data/lib/scout/log.rb +14 -11
  10. data/lib/scout/misc/filesystem.rb +2 -3
  11. data/lib/scout/offsite/ssh.rb +3 -0
  12. data/lib/scout/offsite/step.rb +20 -3
  13. data/lib/scout/open/stream.rb +0 -1
  14. data/lib/scout/open.rb +8 -8
  15. data/lib/scout/path.rb +1 -1
  16. data/lib/scout/persist/serialize.rb +1 -1
  17. data/lib/scout/resource/open.rb +8 -0
  18. data/lib/scout/resource/path.rb +12 -9
  19. data/lib/scout/resource/software.rb +4 -2
  20. data/lib/scout/resource.rb +2 -0
  21. data/lib/scout/tsv/dumper.rb +3 -1
  22. data/lib/scout/tsv/parser.rb +13 -3
  23. data/lib/scout/work_queue.rb +1 -0
  24. data/lib/scout/workflow/deployment/orchestrator.rb +17 -8
  25. data/lib/scout/workflow/step/dependencies.rb +9 -3
  26. data/lib/scout/workflow/step/info.rb +8 -0
  27. data/lib/scout/workflow/step/inputs.rb +5 -0
  28. data/lib/scout/workflow/step/status.rb +22 -2
  29. data/lib/scout/workflow/step.rb +11 -4
  30. data/lib/scout/workflow/task/dependencies.rb +2 -0
  31. data/lib/scout/workflow/task/inputs.rb +14 -9
  32. data/lib/scout/workflow/task.rb +4 -2
  33. data/lib/scout/workflow/usage.rb +21 -33
  34. data/lib/scout/workflow.rb +17 -13
  35. data/scout-gear.gemspec +9 -3
  36. data/scout_commands/resource/produce +66 -0
  37. data/scout_commands/template +52 -0
  38. data/scout_commands/workflow/install +3 -0
  39. data/scout_commands/workflow/task +24 -5
  40. data/share/software/install_helpers +2 -2
  41. data/share/templates/command +25 -0
  42. data/share/templates/workflow.rb +14 -0
  43. data/test/scout/offsite/test_step.rb +0 -1
  44. data/test/scout/test_persist.rb +2 -2
  45. data/test/scout/test_work_queue.rb +1 -1
  46. data/test/scout/tsv/test_parser.rb +21 -0
  47. data/test/scout/workflow/step/test_info.rb +0 -1
  48. data/test/scout/workflow/task/test_dependencies.rb +2 -0
  49. data/test/scout/workflow/task/test_inputs.rb +0 -1
  50. data/test/scout/workflow/test_definition.rb +1 -1
  51. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ca509bfb5065aabcdae73545139db0f8b04748084821e002c0475a55be5cb70
4
- data.tar.gz: 6f0333ca6dd0ceee0ea9290cf3c30f7164c2f0732b76ce739e29da19a0687242
3
+ metadata.gz: 98b3f84350f2f451b717f004bfc97cd7767f7e0fed58e1a2d0cd023bc700185f
4
+ data.tar.gz: '00847ad4d92b1cab22305ea867dc12c870b22e192e79536917f82aa90cc82989'
5
5
  SHA512:
6
- metadata.gz: 2cbd82d97ec0dad50e624cda26ae6a05aeba8b9e5ff07a5b4746c677b8e2ab1996378237095ba09a70f8ed8cc926d782c1677aa24d6f94784f560090d6845a2c
7
- data.tar.gz: aa9529b3393016c846b578b736428b964519d2fb5c1507a6335eff651d0588a5d9191cc61cff52279c18902f2efce386415099bcd92f8736f6d67cef1d33b561
6
+ metadata.gz: 4af5e13873fa13dea33161e9200aa725bb4329899e9242ae6504ffae2ab82e7eb01b82a53edc2a4331ede06d0f12bb07f86f579cb29ecbf0d36e791bf6d53f3e
7
+ data.tar.gz: fc773eb933e5c851aa1d642e872df02d282fbcb29ee1b8e1bf150ffebf9bd4ce8c3670d43a284ff2c8197477a8cf6f71dfcd49b265ad938c723292ec57579998
data/.vimproject CHANGED
@@ -10,6 +10,7 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
10
10
  glob
11
11
  doc
12
12
  update
13
+ template
13
14
  offsite
14
15
  workflow=workflow{
15
16
  task
@@ -17,6 +18,9 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
17
18
  info
18
19
  install
19
20
  }
21
+ resource=resource{
22
+ produce
23
+ }
20
24
  }
21
25
  lib=lib {
22
26
  scout-gear.rb
@@ -115,6 +119,7 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
115
119
  progress.rb
116
120
  provenance.rb
117
121
  status.rb
122
+ inputs.rb
118
123
  }
119
124
  task.rb
120
125
  task=task{
@@ -168,6 +173,9 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
168
173
  sync.rb
169
174
  step.rb
170
175
  }
176
+ hpc=hpc{
177
+ slurm.rb
178
+ }
171
179
  }
172
180
  }
173
181
  test=test {
@@ -198,7 +206,18 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
198
206
  test_task.rb
199
207
  test_usage.rb
200
208
  test_util.rb
209
+ step=step{
210
+ test_dependencies.rb
211
+ test_info.rb
212
+ test_load.rb
213
+ test_provenance.rb
214
+ test_status.rb
201
215
  }
216
+ task=task{
217
+ test_dependencies.rb
218
+ test_inputs.rb
219
+ }
220
+ }
202
221
  indiferent_hash=indiferent_hash{
203
222
  test_case_insensitive.rb
204
223
  test_options.rb
@@ -209,6 +228,9 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
209
228
  software=software{
210
229
  install_helpers
211
230
  }
231
+ templates=templates{
232
+ workflow.rb
233
+ }
212
234
  }
213
235
 
214
236
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 8.1.0
1
+ 9.0.0
data/bin/scout CHANGED
@@ -54,6 +54,7 @@ if dev_dir
54
54
  end
55
55
  end
56
56
 
57
+ require 'scout'
57
58
 
58
59
  require 'scout/simple_opt'
59
60
 
data/lib/rbbt-scout.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  $LOAD_PATH.unshift File.join(__dir__, '../modules/rbbt-util/lib')
2
2
  module Rbbt
3
3
  extend Resource
4
- self.pkgdir = 'rbbt'
5
4
  self.path_maps = Path.path_maps.merge(:rbbt_lib => File.expand_path(File.join(__dir__, '../modules/rbbt-util/', '{TOPLEVEL}','{SUBPATH}')))
6
5
  end
6
+
7
+ Resource.set_software_env Rbbt.software
data/lib/scout/cmd.rb CHANGED
@@ -20,17 +20,22 @@ module CMD
20
20
  end
21
21
  end
22
22
 
23
+
23
24
  def self.get_tool(tool)
24
25
  return tool.to_s unless TOOLS[tool]
25
26
 
26
27
  @@init_cmd_tool ||= IndiferentHash.setup({})
28
+
29
+ claim, test, block, cmd = TOOLS[tool]
30
+ cmd = tool.to_s if cmd.nil?
31
+
27
32
  if !@@init_cmd_tool[tool]
28
- claim, test, block, cmd = TOOLS[tool]
33
+
29
34
  begin
30
35
  if test
31
36
  CMD.cmd(test + " ")
32
37
  else
33
- CMD.cmd("#{tool} --help")
38
+ CMD.cmd("#{cmd} --help")
34
39
  end
35
40
  rescue
36
41
  if claim
@@ -43,7 +48,7 @@ module CMD
43
48
  version = nil
44
49
  ["--version", "-version", "--help", ""].each do |f|
45
50
  begin
46
- version_txt += CMD.cmd("#{tool} #{f} 2>&1", :nofail => true).read
51
+ version_txt += CMD.cmd("#{cmd} #{f} 2>&1", :nofail => true).read
47
52
  version = CMD.scan_version_text(version_txt, tool)
48
53
  break if version
49
54
  rescue
@@ -56,9 +61,8 @@ module CMD
56
61
  return cmd if cmd
57
62
  end
58
63
 
59
- tool.to_s
64
+ cmd
60
65
  end
61
-
62
66
  def self.scan_version_text(text, cmd = nil)
63
67
  cmd = "NOCMDGIVE" if cmd.nil? || cmd.empty?
64
68
  text = Misc.fixutf8 text
@@ -75,5 +75,22 @@ module IndiferentHash
75
75
  end
76
76
  clean
77
77
  end
78
+
79
+ def slice(*list)
80
+ ext_list = []
81
+ list.each do |e|
82
+ case e
83
+ when Symbol
84
+ ext_list << e
85
+ ext_list << e.to_s
86
+ when String
87
+ ext_list << e
88
+ ext_list << e.to_sym
89
+ else
90
+ ext_list << e
91
+ end
92
+ end
93
+ IndiferentHash.setup(super(*ext_list))
94
+ end
78
95
  end
79
96
 
@@ -161,6 +161,7 @@ module Log
161
161
  def save
162
162
  info = {:start => @start, :last_time => @last_time, :last_count => @last_count, :last_percent => @last_percent, :desc => @desc, :ticks => @ticks, :max => @max, :mean => @mean}
163
163
  info.delete_if{|k,v| v.nil?}
164
+ FileUtils.mkdir_p File.dirname(file) unless File.exist?(File.dirname(file))
164
165
  File.write(file, info.to_yaml)
165
166
  end
166
167
 
data/lib/scout/log.rb CHANGED
@@ -51,17 +51,19 @@ module Log
51
51
 
52
52
  def self.tty_size
53
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
54
+ size = 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
+ size = size.to_i if String === size
66
+ size
65
67
  end
66
68
  end
67
69
 
@@ -269,6 +271,7 @@ module Log
269
271
  line = line.sub('`',"'")
270
272
  color = :green if line =~ /workflow/
271
273
  color = :blue if line =~ /scout-/
274
+ color = :cyan if line =~ /rbbt-/
272
275
  if color
273
276
  Log.color color, line
274
277
  else
@@ -16,11 +16,10 @@ module Misc
16
16
 
17
17
  basedir += "/" unless basedir.slice(-2,-1) == "/"
18
18
 
19
- if path.index(basedir) == 0
20
- return path[basedir.length..-1]
19
+ if path.start_with?(basedir)
20
+ return path.slice(basedir.length, basedir.length)
21
21
  else
22
22
  return nil
23
23
  end
24
24
  end
25
-
26
25
  end
@@ -21,6 +21,9 @@ class SSHLine
21
21
  ch.exec 'bash -l'
22
22
  end
23
23
 
24
+ @ch.send_data("[[ -f ~/.scout/environment ]] && source ~/.scout/environment\n")
25
+ @ch.send_data("[[ -f ~/.rbbt/environment ]] && source ~/.rbbt/environment\n")
26
+
24
27
  @ch.on_data do |_,data|
25
28
  if m = data.match(/DONECMD: (\d+)\n/)
26
29
  @exit_status = m[1].to_i
@@ -5,14 +5,14 @@ require_relative 'sync'
5
5
  module OffsiteStep
6
6
 
7
7
  extend MetaExtension
8
- extension_attr :server, :workflow_name, :clean_id, :provided_inputs
8
+ extension_attr :server, :workflow_name, :clean_id, :slurm
9
9
 
10
10
  def inputs_directory
11
11
  @inputs_directory ||= begin
12
12
  if provided_inputs && provided_inputs.any?
13
13
  file = ".scout/tmp/step_inputs/#{workflow}/#{task_name}/#{name}"
14
14
  TmpFile.with_path do |inputs_dir|
15
- task.save_inputs(inputs_dir, provided_inputs)
15
+ save_inputs(inputs_dir)
16
16
  SSHLine.rsync(inputs_dir, file, target: server, directory: true)
17
17
  end
18
18
  file
@@ -68,6 +68,19 @@ job = wf.job(:#{task_name}, "#{clean_name}");
68
68
  status == :done
69
69
  end
70
70
 
71
+ def orchestrate_slurm
72
+ bundle_files = offsite_job_ssh <<~EOF
73
+ require 'rbbt/hpc'
74
+ HPC::BATCH_MODULE = HPC.batch_system "SLURM"
75
+ HPC::BATCH_MODULE.orchestrate_job(job, {})
76
+ job.join
77
+ job.bundle_files
78
+ EOF
79
+ SSHLine.sync(bundle_files, source: server)
80
+ self.load
81
+ end
82
+
83
+
71
84
  def exec
72
85
  bundle_files = offsite_job_ssh <<~EOF
73
86
  job.run
@@ -78,6 +91,10 @@ job = wf.job(:#{task_name}, "#{clean_name}");
78
91
  end
79
92
 
80
93
  def run
81
- exec
94
+ if slurm
95
+ orchestrate_slurm
96
+ else
97
+ exec
98
+ end
82
99
  end
83
100
  end
@@ -66,7 +66,6 @@ module Open
66
66
  into.close if into and into_close and not into.closed?
67
67
  block.call if block_given?
68
68
 
69
- Log.debug "Consume stream done #{Log.fingerprint io} -> #{Log.fingerprint(into_path ||into)}"
70
69
  last_c
71
70
  rescue Aborted
72
71
  Thread.current["exception"] = true
data/lib/scout/open.rb CHANGED
@@ -20,21 +20,21 @@ module Open
20
20
  end
21
21
  end
22
22
 
23
- def self.get_stream(file, mode = 'r')
23
+ def self.get_stream(file, mode = 'r', options = {})
24
24
  return file if Open.is_stream?(file)
25
25
  return file.stream if Open.has_stream?(file)
26
26
  file = file.find if Path === file
27
27
 
28
- return Open.ssh(file) if Open.ssh?(file)
29
- return Open.wget(file) if Open.remote?(file)
28
+ return Open.ssh(file, options) if Open.ssh?(file)
29
+ return Open.wget(file, options) if Open.remote?(file)
30
30
 
31
31
  File.open(file, mode)
32
32
  end
33
33
 
34
- def self.file_open(file, grep = false, mode = 'r', invert_grep = false)
34
+ def self.file_open(file, grep = false, mode = 'r', invert_grep = false, options = {})
35
35
  Open.mkdir File.dirname(file) if mode.include? 'w'
36
36
 
37
- stream = get_stream(file, mode)
37
+ stream = get_stream(file, mode, options)
38
38
 
39
39
  if grep
40
40
  grep(stream, grep, invert_grep)
@@ -58,7 +58,7 @@ module Open
58
58
  def self.open(file, options = {})
59
59
  if IO === file || StringIO === file
60
60
  if block_given?
61
- res = yield file
61
+ res = yield file, options
62
62
  file.close
63
63
  return res
64
64
  else
@@ -72,7 +72,7 @@ module Open
72
72
 
73
73
  options[:noz] = true if mode.include? "w"
74
74
 
75
- io = file_open(file, options[:grep], mode, options[:invert_grep])
75
+ io = file_open(file, options[:grep], mode, options[:invert_grep], options)
76
76
 
77
77
  io = unzip(io) if ((String === file and zip?(file)) and not options[:noz]) or options[:zip]
78
78
  io = gunzip(io) if ((String === file and gzip?(file)) and not options[:noz]) or options[:gzip]
@@ -88,7 +88,7 @@ module Open
88
88
  rescue DontClose
89
89
  res = $!.payload
90
90
  rescue Exception
91
- io.abort if io.respond_to? :abort
91
+ io.abort $! if io.respond_to? :abort
92
92
  io.join if io.respond_to? :join
93
93
  raise $!
94
94
  ensure
data/lib/scout/path.rb CHANGED
@@ -32,7 +32,7 @@ module Path
32
32
  prevpath = prevpath.to_s if Symbol === prevpath
33
33
 
34
34
  subpath = File.join(prevpath.to_s, subpath) if prevpath
35
- new = self.empty? ? subpath : File.join(self, subpath)
35
+ new = self.empty? ? subpath.dup : File.join(self, subpath)
36
36
  self.annotate(new)
37
37
  new
38
38
  end
@@ -104,7 +104,7 @@ module Persist
104
104
 
105
105
  if type == :binary
106
106
  content.force_encoding("ASCII-8BIT") if content.respond_to? :force_encoding
107
- Open.open(path, :mode => 'wb') do |f|
107
+ Open.open(file, :mode => 'wb') do |f|
108
108
  f.puts content
109
109
  end
110
110
  content
@@ -0,0 +1,8 @@
1
+ class << Open
2
+ alias _just_open open
3
+
4
+ def open(file, *args, **kwargs, &block)
5
+ file.produce if Path === file
6
+ _just_open(file, *args, **kwargs, &block)
7
+ end
8
+ end
@@ -3,7 +3,7 @@ module Path
3
3
  return self if ! force && (Open.exist?(self) || @produced)
4
4
  begin
5
5
  if Resource === self.pkgdir
6
- self.pkgdir.produce self
6
+ self.pkgdir.produce self, force
7
7
  else
8
8
  false
9
9
  end
@@ -13,7 +13,7 @@ module Path
13
13
  message = $!.message
14
14
  message = "No exception message" if message.nil? || message.empty?
15
15
  Log.warn "Error producing #{self}: #{message}"
16
- false
16
+ raise $!
17
17
  ensure
18
18
  @produced = true
19
19
  end
@@ -33,13 +33,16 @@ module Path
33
33
  end
34
34
 
35
35
  def produce_and_find(extension = nil, *args)
36
- if extension
37
- found = find_with_extension(extension, *args)
38
- found.exists? ? found : produce_with_extension(extension, *args)
39
- else
40
- found = find
41
- found.exists? ? found : produce(*args)
42
- end
36
+ found = if extension
37
+ found = find_with_extension(extension, *args)
38
+ found.exists? ? found : produce_with_extension(extension, *args)
39
+ else
40
+ found = find
41
+ found.exists? ? found : produce(*args)
42
+ end
43
+ raise "Not found: #{self}" unless found
44
+
45
+ found
43
46
  end
44
47
 
45
48
  def relocate
@@ -6,7 +6,7 @@ module Resource
6
6
 
7
7
  def self.install(content, name, software_dir = Path.setup('software'), &block)
8
8
  software_dir ||= Path.setup('software')
9
- software_dir = software_dir.find if Path === software_dir
9
+ software_dir = software_dir.find(:user) if Path === software_dir
10
10
 
11
11
  content = block if block_given?
12
12
 
@@ -166,11 +166,13 @@ url='#{url}'
166
166
  value.sub!(/^['"]/,'')
167
167
  value.sub!(/['"]$/,'')
168
168
  value.gsub!(/\$[a-z_0-9]+/i){|var| ENV[var[1..-1]] }
169
- Log.debug "Set variable export from .post_install: #{Misc.fingerprint [var,value]*"="}"
169
+ Log.debug "Set variable export from .post_install: #{Log.fingerprint [var,value]*"="}"
170
170
  ENV[var] = value
171
171
  end
172
172
  end
173
173
  end
174
174
  end
175
175
  end
176
+
177
+ self.set_software_env
176
178
  end
@@ -2,7 +2,9 @@ require_relative 'log'
2
2
  require_relative 'path'
3
3
  require_relative 'resource/produce'
4
4
  require_relative 'resource/path'
5
+ require_relative 'resource/open'
5
6
  require_relative 'resource/util'
7
+ require_relative 'resource/software'
6
8
 
7
9
  module Resource
8
10
  extend MetaExtension
@@ -11,7 +11,9 @@ module TSV
11
11
  fields_str = "#{header_hash}#{key_field || "Id"}#{sep}#{fields*sep}"
12
12
  end
13
13
 
14
- if preamble && options.values.compact.any?
14
+ if String === preamble
15
+ preamble_str = preamble
16
+ elsif preamble && options.values.compact.any?
15
17
  preamble_str = "#: " << IndiferentHash.hash2string(options)
16
18
  else
17
19
  preamble_str = nil
@@ -51,7 +51,7 @@ module TSV
51
51
  [key, items]
52
52
  end
53
53
 
54
- def self.parse_stream(stream, data: nil, source_type: nil, type: :list, merge: true, one2one: false, fix: true, bar: false, first_line: nil, field_names: nil, **kargs, &block)
54
+ def self.parse_stream(stream, data: nil, source_type: nil, type: :list, merge: true, one2one: false, fix: true, bar: false, first_line: nil, field_names: nil, head: nil, **kargs, &block)
55
55
  begin
56
56
  bar = "Parsing #{Log.fingerprint stream}" if TrueClass === bar
57
57
  bar = Log::ProgressBar.get_obj_bar(stream, bar) if bar
@@ -62,7 +62,8 @@ module TSV
62
62
  data = {} if data.nil?
63
63
  merge = false if type != :double && type != :flat
64
64
  line = first_line || stream.gets
65
- while line
65
+ while line
66
+ break if head && head <= 0
66
67
  begin
67
68
  line.chomp!
68
69
  if Proc === fix
@@ -171,6 +172,7 @@ module TSV
171
172
  stream.abort($!) if stream.respond_to?(:abort)
172
173
  raise $!
173
174
  ensure
175
+ head = head - 1 if head
174
176
  if stream.closed?
175
177
  line = nil
176
178
  else
@@ -185,7 +187,15 @@ module TSV
185
187
  else
186
188
  bar.remove
187
189
  end if bar
188
- stream.join if stream.respond_to?(:join)
190
+
191
+ if stream.respond_to?(:join)
192
+ eof = begin
193
+ stream.eof?
194
+ rescue IOError
195
+ true
196
+ end
197
+ stream.join if eof
198
+ end
189
199
  end
190
200
  end
191
201
 
@@ -6,6 +6,7 @@ class WorkQueue
6
6
  attr_accessor :workers, :worker_proc, :callback
7
7
 
8
8
  def initialize(workers = 0, &block)
9
+ workers = workers.to_i if String === workers
9
10
  @input = WorkQueue::Socket.new
10
11
  @output = WorkQueue::Socket.new
11
12
  @workers = workers.times.collect{ Worker.new }
@@ -6,7 +6,7 @@ module Workflow
6
6
  return workload if job.done? && job.updated?
7
7
 
8
8
  job.dependencies.each do |dep|
9
- next if dep.done? && job.updated?
9
+ next if dep.done? && dep.updated?
10
10
  workload.merge!(job_workload(dep))
11
11
  workload[job] += workload[dep]
12
12
  workload[job] << dep
@@ -14,7 +14,7 @@ module Workflow
14
14
  end
15
15
 
16
16
  job.input_dependencies.each do |dep|
17
- next if dep.done? && job.updated?
17
+ next if dep.done? && dep.updated?
18
18
  workload.merge!(job_workload(dep))
19
19
  workload[job] += workload[dep]
20
20
  workload[job] << dep
@@ -85,15 +85,15 @@ module Workflow
85
85
  candidates = workload.
86
86
  select{|k,v| v.empty? }.
87
87
  collect{|k,v| k }.
88
- reject{|k| k.done? }
88
+ reject{|k| k.done? || k.running? }
89
89
  else
90
90
  candidates = workload. #select{|k,v| Orchestrator.job_rules(rules, k) }.
91
91
  select{|k,v| v.empty? }.
92
92
  collect{|k,v| k }.
93
- reject{|k| k.done? }
93
+ reject{|k| k.done? || k.running? }
94
94
  end
95
95
 
96
- top_level = workload.keys - workload.values.flatten
96
+ #top_level = workload.keys - workload.values.flatten
97
97
 
98
98
  candidates = purge_duplicates candidates
99
99
  candidates = sort_candidates candidates, rules
@@ -192,7 +192,10 @@ module Workflow
192
192
  workload = Orchestrator.workload(jobs)
193
193
  all_jobs = workload.keys
194
194
 
195
+ all_jobs.each{|job| job.clean unless job.done? && job.updated? }
196
+
195
197
  top_level_jobs = jobs.collect{|job| job.path }
198
+ failed_jobs = []
196
199
  while workload.any?
197
200
 
198
201
  candidates = resources_used.keys + Orchestrator.candidates(workload, rules)
@@ -203,9 +206,15 @@ module Workflow
203
206
  case
204
207
  when (job.error? || job.aborted?)
205
208
  begin
206
- if job.recoverable_error?
207
- job.clean
208
- raise TryAgain
209
+ if job.recoverable_error?
210
+ if failed_jobs.include?(job)
211
+ Log.warn "Failed twice #{job.path} with recoverable error"
212
+ next
213
+ else
214
+ failed_jobs << job
215
+ job.clean
216
+ raise TryAgain
217
+ end
209
218
  else
210
219
  next
211
220
  end
@@ -1,7 +1,13 @@
1
1
  class Step
2
- def rec_dependencies
3
- rec_dependencies = dependencies.dup
4
- dependencies.inject(rec_dependencies){|acc,d| acc.concat d.rec_dependencies }
2
+ def rec_dependencies(connected = false, seen = [])
3
+ direct_deps = []
4
+ dependencies.each do |dep|
5
+ next if seen.include? dep.path
6
+ next if connected && dep.done? && dep.updated?
7
+ direct_deps << dep
8
+ end
9
+ seen.concat direct_deps.collect{|d| d.path }
10
+ direct_deps.inject(direct_deps){|acc,d| acc.concat(d.rec_dependencies(connected, [])); acc }
5
11
  end
6
12
 
7
13
  def recursive_inputs
@@ -127,6 +127,14 @@ class Step
127
127
  ! done? && (info[:pid] && Misc.pid_alive?(info[:pid]))
128
128
  end
129
129
 
130
+ def overriden?
131
+ overriden_task || overriden_workflow || dependencies.select{|d| d.overriden? }.any?
132
+ end
133
+
134
+ def overriden_deps
135
+ rec_dependencies.select{|d| d.overriden? }
136
+ end
137
+
130
138
  def exception
131
139
  info[:exception]
132
140
  end
@@ -0,0 +1,5 @@
1
+ class Step
2
+ def save_inputs(inputs_dir)
3
+ self.task.save_inputs(inputs_dir, provided_inputs)
4
+ end
5
+ end