libis-workflow 2.0.beta.13 → 2.0.beta.14

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.
@@ -15,69 +15,82 @@ module Libis
15
15
  include ::Libis::Workflow::Base::Logger
16
16
  include ::Libis::Tools::ParameterContainer
17
17
 
18
- attr_accessor :parent, :name, :workitem, :tasks
18
+ attr_accessor :parent, :name, :workitem
19
19
 
20
20
  parameter quiet: false, description: 'Prevemt generating log output.'
21
- parameter abort_on_error: false, description: 'Stop all tasks when an error occurs.'
22
- parameter always_run: false, description: 'Run this task, even if the item failed a previous task.'
23
- parameter subitems: false, description: 'Do not process the given item, but only the subitems.'
24
21
  parameter recursive: false, description: 'Run the task on all subitems recursively.'
22
+ parameter retry_count: 0, description: 'Number of times to retry the task.'
23
+ parameter retry_interval: 10, description: 'Number of seconds to wait between retries.'
25
24
 
26
25
  def self.task_classes
27
- ObjectSpace.each_object(::Class).select {|klass| klass < self}
26
+ ObjectSpace.each_object(::Class).select { |klass| klass < self }
28
27
  end
29
28
 
30
29
  def initialize(parent, cfg = {})
31
30
  @subitems_stopper = false
32
31
  @subtasks_stopper = false
33
32
  self.parent = parent
34
- self.tasks = []
35
33
  configure cfg
36
34
  end
37
35
 
38
36
  def <<(task)
39
- self.tasks << task
37
+ raise Libis::WorkflowError, "Processing task '#{self.namepath}' is not allowed to have subtasks."
40
38
  end
41
39
 
40
+ # @param [Libis::Workflow::Base::WorkItem] item
42
41
  def run(item)
43
-
44
42
  check_item_type ::Libis::Workflow::Base::WorkItem, item
43
+ self.workitem = item
45
44
 
46
- return if item.failed? unless parameter(:always_run)
45
+ case self.action
46
+ when :retry
47
+ return if item.check_status(:DONE, self.namepath)
48
+ when :failed
49
+ return
50
+ else
51
+ end
52
+
53
+ (parameter(:retry_count)+1).times do
47
54
 
48
- if parameter(:subitems)
49
- log_started item
50
- run_subitems item
51
- log_done(item) unless item.failed?
52
- else
53
55
  run_item(item)
54
- end
55
56
 
56
- item.save
57
+ case item.status(self.namepath)
58
+ when :DONE
59
+ self.action = :run
60
+ return
61
+ when :ASYNC_WAIT
62
+ self.action = :retry
63
+ when :ASYNC_HALT
64
+ break
65
+ when :FAILED
66
+ break
67
+ else
68
+ return
69
+ end
57
70
 
58
- end
71
+ self.action = :retry
59
72
 
60
- def run_item(item)
73
+ sleep(parameter(:retry_interval))
61
74
 
62
- begin
75
+ end
63
76
 
64
- self.workitem = item
77
+ item.get_run.action = :failed
65
78
 
66
- process_item item
79
+ rescue WorkflowError => e
80
+ error e.message, item
81
+ update_status item, :FAILED
67
82
 
68
- rescue WorkflowError => e
69
- error e.message
70
- log_failed item
83
+ rescue WorkflowAbort => e
84
+ update_status item, :FAILED
85
+ raise e if parent
71
86
 
72
- rescue WorkflowAbort => e
73
- item.status = to_status :failed
74
- raise e if parent
87
+ rescue ::Exception => e
88
+ update_status item, :FAILED
89
+ fatal "Exception occured: #{e.message}", item
90
+ debug e.backtrace.join("\n")
75
91
 
76
- rescue ::Exception => e
77
- fatal 'Exception occured: %s', e.message
78
- debug e.backtrace.join("\n")
79
- log_failed item
80
- end
92
+ ensure
93
+ item.save
81
94
 
82
95
  end
83
96
 
@@ -85,7 +98,9 @@ module Libis
85
98
  (self.parent.names rescue Array.new).push(name).compact
86
99
  end
87
100
 
88
- def namepath; self.names.join('/'); end
101
+ def namepath;
102
+ self.names.join('/');
103
+ end
89
104
 
90
105
  def apply_options(opts)
91
106
  o = {}
@@ -103,74 +118,35 @@ module Libis
103
118
  end
104
119
  end
105
120
 
106
- self.tasks.each do |task|
107
- task.apply_options opts
108
- end
109
121
  end
110
122
 
111
123
  protected
112
124
 
113
- def stop_processing_subitems
114
- @subitems_stopper = true if parameter(:recursive)
115
- end
116
-
117
- def check_processing_subitems
118
- if @subitems_stopper
119
- @subitems_stopper = false
120
- return false
125
+ def configure(cfg)
126
+ self.name = cfg[:name] || (cfg[:class] || self.class).to_s.split('::').last
127
+ (cfg[:options] || {}).merge(
128
+ cfg.reject { |k, _| [:options, :name, :class].include? k.to_sym }
129
+ ).symbolize_keys.each do |k, v|
130
+ self.parameter(k, v)
121
131
  end
122
- true
123
132
  end
124
133
 
125
- def stop_processing_subtasks
126
- @subtasks_stopper= true
127
- end
134
+ def run_item(item)
135
+ @item_skipper = false
128
136
 
129
- def check_processing_subtasks
130
- if @subtasks_stopper
131
- @subtasks_stopper = false
132
- return false
133
- end
134
- true
135
- end
137
+ pre_process(item)
136
138
 
137
- def skip_processing_item
138
- @item_skipper = true
139
- end
139
+ set_status item, :STARTED
140
140
 
141
- def log_started(item)
142
- item.status = to_status :started
143
- end
141
+ self.process item unless @item_skipper
144
142
 
145
- def log_failed(item, message = nil)
146
- warn (message), item if message
147
- item.status = to_status :failed
148
- end
143
+ run_subitems(item) if parameter(:recursive)
149
144
 
150
- def log_done(item)
151
- item.status = to_status :done
152
- end
145
+ update_status item, :DONE
153
146
 
154
- def process_item(item)
155
- @item_skipper = false
156
- pre_process(item)
157
- unless @item_skipper
158
- log_started item
159
- process item
160
- end
161
- run_subitems(item) if parameter(:recursive)
162
- unless @item_skipper
163
- run_subtasks item
164
- log_done item unless item.failed?
165
- end
166
147
  post_process item
167
148
  end
168
149
 
169
- def process(item)
170
- # needs implementation unless there are subtasks
171
- raise RuntimeError, 'Should be overwritten' if self.tasks.empty?
172
- end
173
-
174
150
  def pre_process(_)
175
151
  true
176
152
  # optional implementation
@@ -180,12 +156,39 @@ module Libis
180
156
  # optional implementation
181
157
  end
182
158
 
183
- def get_root_item
184
- self.workitem.root
159
+ def run_subitems(parent_item)
160
+ return unless check_processing_subitems
161
+ items = subitems parent_item
162
+ return unless items.count > 0
163
+
164
+ status = Hash.new(0)
165
+ items.each_with_index do |item, i|
166
+ debug 'Processing subitem (%d/%d): %s', parent_item, i+1, items.count, item.to_s
167
+ run_item item
168
+ status[item.status(self.namepath)] += 1
169
+ end
170
+
171
+ debug '%d of %d subitems passed', parent_item, status[:DONE], items.count
172
+ substatus_check(status, parent_item, 'item')
185
173
  end
186
174
 
187
- def get_work_dir
188
- get_root_item.work_dir
175
+ def substatus_check(status, item, task_or_item)
176
+ if (failed = status[:FAILED] > 0)
177
+ warn "%d sub#{task_or_item}(s) failed", item, failed
178
+ update_status(item, :FAILED)
179
+ end
180
+
181
+ if (halted = status[:ASYNC_HALT] > 0)
182
+ warn "%d sub#{task_or_item}(s) halted in async process", item, halted
183
+ update_status(item, :ASYNC_HALT)
184
+ end
185
+
186
+ if (waiting = status[:ASYNC_WAIT] > 0)
187
+ warn "waiting for %d sub#{task_or_item}(s) in async process", item, waiting
188
+ update_status(item, :ASYNC_WAIT)
189
+ end
190
+
191
+ update_status(item, :DONE)
189
192
  end
190
193
 
191
194
  def capture_cmd(cmd, *opts)
@@ -200,61 +203,50 @@ module Libis
200
203
  $stderr = STDERR
201
204
  end
202
205
 
203
- def run_subitems(parent_item)
204
- return unless check_processing_subitems
205
- items = subitems parent_item
206
- failed = passed = 0
207
- items.each_with_index do |item, i|
208
- debug 'Processing subitem (%d/%d): %s', parent_item, i+1, items.count, item.to_s
209
- run_item item
210
- if item.failed?
211
- failed += 1
212
- if parameter(:abort_on_error)
213
- error 'Aborting ...', parent_item
214
- raise WorkflowAbort.new "Aborting: task #{name} failed on #{item}"
215
- end
216
- else
217
- passed += 1
218
- end
219
- end
220
- if failed > 0
221
- warn '%d subitem(s) failed', parent_item, failed
222
- if failed == items.count
223
- error 'All subitems have failed', parent_item
224
- log_failed parent_item
225
- return
226
- end
227
- end
228
- debug '%d of %d subitems passed', parent_item, passed, items.count if items.count > 0
206
+ def action=(action)
207
+ self.workitem.get_run.action = action
229
208
  end
230
209
 
231
- def run_subtasks(item)
232
- return unless check_processing_subtasks
233
- tasks = subtasks item
234
- tasks.each_with_index do |task, i|
235
- debug 'Running subtask (%d/%d): %s', item, i+1, tasks.count, task.name
236
- task.run item
237
- if item.failed?
238
- if task.parameter(:abort_on_error)
239
- error 'Aborting ...'
240
- raise WorkflowAbort.new "Aborting: task #{task.name} failed on #{item}"
241
- end
242
- return
243
- end
244
- end
210
+ def action
211
+ self.workitem.get_run.action
245
212
  end
246
213
 
247
- def configure(cfg)
248
- self.name = cfg[:name] || (cfg[:class] || self.class).to_s.split('::').last
249
- (cfg[:options] || {}).merge(
250
- cfg.reject { |k, _| [:options, :name, :class].include? k.to_sym }
251
- ).symbolize_keys.each do |k,v|
252
- self.parameter(k,v)
214
+ def get_root_item(item = nil)
215
+ (item || self.workitem).get_root
216
+ end
217
+
218
+ def get_work_dir(item = nil)
219
+ get_root_item(item).work_dir
220
+ end
221
+
222
+ def stop_processing_subitems
223
+ @subitems_stopper = true if parameter(:recursive)
224
+ end
225
+
226
+ def check_processing_subitems
227
+ if @subitems_stopper
228
+ @subitems_stopper = false
229
+ return false
253
230
  end
231
+ true
232
+ end
233
+
234
+ def skip_processing_item
235
+ @item_skipper = true
236
+ end
237
+
238
+ def update_status(item, state)
239
+ return nil unless item.compare_status(state, self.namepath) < 0
240
+ set_status(item, state)
241
+ end
242
+
243
+ def set_status(item, state)
244
+ item.status = to_status(state)
245
+ state
254
246
  end
255
247
 
256
- def to_status(text)
257
- [text.to_s.capitalize, self.names]
248
+ def to_status(state)
249
+ [state, self.namepath]
258
250
  end
259
251
 
260
252
  def check_item_type(klass, item = nil)
@@ -271,16 +263,12 @@ module Libis
271
263
 
272
264
  private
273
265
 
274
- def subtasks(item = nil)
275
- self.tasks.map do |task|
276
- ((item || self.workitem).failed? and not task.parameter(:always_run)) ? nil : task
277
- end.compact
266
+ def subtasks
267
+ self.tasks
278
268
  end
279
269
 
280
270
  def subitems(item = nil)
281
- items = (item || workitem).items
282
- return items if self.parameter(:always_run)
283
- items.reject { |i| i.failed? }
271
+ (item || workitem).items
284
272
  end
285
273
 
286
274
  def default_values
@@ -288,7 +276,7 @@ module Libis
288
276
  end
289
277
 
290
278
  def self.default_values
291
- parameter_defs.inject({}) do |hash,parameter|
279
+ parameter_defs.inject({}) do |hash, parameter|
292
280
  hash[parameter.first] = parameter.last[:default]
293
281
  hash
294
282
  end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ require_relative 'task'
3
+
4
+ module Libis
5
+ module Workflow
6
+
7
+ # noinspection RubyTooManyMethodsInspection
8
+ class TaskGroup < Libis::Workflow::Task
9
+
10
+ attr_accessor :tasks
11
+
12
+ def initialize(parent, cfg = {})
13
+ self.tasks = []
14
+ super parent, cfg
15
+ end
16
+
17
+ def <<(task)
18
+ self.tasks << task
19
+ end
20
+
21
+ def apply_options(opts)
22
+ super opts
23
+ self.tasks.each do |task|
24
+ task.apply_options opts
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def process(item)
31
+
32
+ return unless check_processing_subtasks
33
+
34
+ tasks = subtasks
35
+ return unless tasks.count > 0
36
+
37
+ status = Hash.new(0)
38
+ tasks.each_with_index do |task, i|
39
+ debug 'Running subtask (%d/%d): %s', item, i+1, tasks.count, task.name
40
+ task.run item
41
+ status[item.status(task.namepath)] += 1
42
+ end
43
+
44
+ substatus_check(status, item, 'task')
45
+ end
46
+
47
+ def stop_processing_subtasks
48
+ @subtasks_stopper= true
49
+ end
50
+
51
+ def check_processing_subtasks
52
+ if @subtasks_stopper
53
+ @subtasks_stopper = false
54
+ return false
55
+ end
56
+ true
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -9,14 +9,12 @@ module Libis
9
9
  class Analyzer < Task
10
10
 
11
11
  parameter quiet: true, frozen: true
12
- parameter abort_on_error: false, frozen: true
13
- parameter always_run: true, frozen: true
14
- parameter subitems: false, frozen: true
15
12
  parameter recursive: false, frozen: true
16
13
 
14
+ # @param [Libis::Workflow::Base::WorkItem] item
17
15
  def run(item)
18
16
 
19
- item.properties[:ingest_failed] = item.failed?
17
+ item.properties[:ingest_failed] = item.check_status(:FAILED)
20
18
 
21
19
  item.summary = {}
22
20
  item.log_history.each do |log|
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Libis
4
4
  module Workflow
5
- VERSION = '2.0.beta.13' unless const_defined? :VERSION # the guard is against a redefinition warning that happens on Travis
5
+ VERSION = '2.0.beta.14' unless const_defined? :VERSION # the guard is against a redefinition warning that happens on Travis
6
6
  end
7
7
  end
@@ -32,17 +32,9 @@ module Libis
32
32
  self.log_history << msg.merge(c_at: ::Time.now)
33
33
  end
34
34
 
35
- def add_status_log(message, tasklist = nil)
35
+ def add_status_log(info)
36
36
  # noinspection RubyResolve
37
- self.status_log << {
38
- timestamp: ::Time.now,
39
- tasklist: tasklist,
40
- text: message
41
- }.cleanup
42
- end
43
-
44
- def status_label(status_entry)
45
- "#{status_entry[:tasklist].last rescue nil}#{status_entry[:text] rescue nil}"
37
+ self.status_log << info.merge(timestamp: ::Time.now).cleanup
46
38
  end
47
39
 
48
40
  end
@@ -11,12 +11,12 @@ module Libis
11
11
  include Sidekiq::Worker
12
12
 
13
13
  def perform(job_config, options = {})
14
- job = self.class.configure(job_config, options)
14
+ job = configure(job_config, options)
15
15
  options[:interactive] = false
16
16
  job.execute options
17
17
  end
18
18
 
19
- def self.configure(job_config, options = {})
19
+ def configure(job_config, options = {})
20
20
  log_path = options.delete :log_path
21
21
  if log_path
22
22
  Config.logger = ::Logger.new(
@@ -25,7 +25,7 @@ module Libis
25
25
  (options.delete(:log_shift_size) || 1024 ** 2)
26
26
  )
27
27
  Config.logger.formatter = ::Logger::Formatter.new
28
- Config.logger.level = ::Logger::DEBUG
28
+ Config.logger.level = (options.delete(:log_level) || ::Logger::DEBUG)
29
29
  end
30
30
  get_job(job_config)
31
31
  end
@@ -12,11 +12,14 @@ module Libis
12
12
  autoload :FileItem, 'libis/workflow/base/file_item'
13
13
  autoload :DirItem, 'libis/workflow/base/dir_item'
14
14
  autoload :Logger, 'libis/workflow/base/logger'
15
+ autoload :Logging, 'libis/workflow/base/logging'
15
16
  autoload :Job, 'libis/workflow/base/job'
16
17
  autoload :Run, 'libis/workflow/base/run'
17
18
  autoload :Workflow, 'libis/workflow/base/workflow'
18
19
  end
19
20
 
21
+ autoload :Status, 'libis/workflow/status'
22
+
20
23
  autoload :WorkItem, 'libis/workflow/work_item'
21
24
  autoload :FileItem, 'libis/workflow/file_item'
22
25
  autoload :DirItem, 'libis/workflow/dir_item'
@@ -25,6 +28,7 @@ module Libis
25
28
  autoload :Job, 'libis/workflow/job'
26
29
  autoload :Run, 'libis/workflow/run'
27
30
  autoload :Task, 'libis/workflow/task'
31
+ autoload :TaskGroup, 'libis/workflow/task_group'
28
32
 
29
33
  autoload :Worker, 'libis/workflow/worker'
30
34
 
data/spec/task_spec.rb CHANGED
@@ -10,7 +10,6 @@ describe 'Task' do
10
10
 
11
11
  expect(task.parent).to eq nil
12
12
  expect(task.name).to eq 'Task'
13
- expect(task.parameter(:abort_on_error)).to eq false
14
13
 
15
14
  end
16
15