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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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