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.
- checksums.yaml +4 -4
- data/README.md +42 -44
- data/lib/libis/workflow/action.rb +24 -0
- data/lib/libis/workflow/base/logging.rb +76 -0
- data/lib/libis/workflow/base/run.rb +11 -7
- data/lib/libis/workflow/base/work_item.rb +30 -108
- data/lib/libis/workflow/base/workflow.rb +10 -11
- data/lib/libis/workflow/base.rb +1 -0
- data/lib/libis/workflow/status.rb +83 -0
- data/lib/libis/workflow/task.rb +133 -145
- data/lib/libis/workflow/task_group.rb +62 -0
- data/lib/libis/workflow/tasks/analyzer.rb +2 -4
- data/lib/libis/workflow/version.rb +1 -1
- data/lib/libis/workflow/work_item.rb +2 -10
- data/lib/libis/workflow/worker.rb +3 -3
- data/lib/libis/workflow.rb +4 -0
- data/spec/task_spec.rb +0 -1
- data/spec/workflow_spec.rb +42 -39
- metadata +6 -2
data/lib/libis/workflow/task.rb
CHANGED
@@ -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
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
71
|
+
self.action = :retry
|
59
72
|
|
60
|
-
|
73
|
+
sleep(parameter(:retry_interval))
|
61
74
|
|
62
|
-
|
75
|
+
end
|
63
76
|
|
64
|
-
|
77
|
+
item.get_run.action = :failed
|
65
78
|
|
66
|
-
|
79
|
+
rescue WorkflowError => e
|
80
|
+
error e.message, item
|
81
|
+
update_status item, :FAILED
|
67
82
|
|
68
|
-
|
69
|
-
|
70
|
-
|
83
|
+
rescue WorkflowAbort => e
|
84
|
+
update_status item, :FAILED
|
85
|
+
raise e if parent
|
71
86
|
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
77
|
-
|
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;
|
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
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
126
|
-
@
|
127
|
-
end
|
134
|
+
def run_item(item)
|
135
|
+
@item_skipper = false
|
128
136
|
|
129
|
-
|
130
|
-
if @subtasks_stopper
|
131
|
-
@subtasks_stopper = false
|
132
|
-
return false
|
133
|
-
end
|
134
|
-
true
|
135
|
-
end
|
137
|
+
pre_process(item)
|
136
138
|
|
137
|
-
|
138
|
-
@item_skipper = true
|
139
|
-
end
|
139
|
+
set_status item, :STARTED
|
140
140
|
|
141
|
-
|
142
|
-
item.status = to_status :started
|
143
|
-
end
|
141
|
+
self.process item unless @item_skipper
|
144
142
|
|
145
|
-
|
146
|
-
warn (message), item if message
|
147
|
-
item.status = to_status :failed
|
148
|
-
end
|
143
|
+
run_subitems(item) if parameter(:recursive)
|
149
144
|
|
150
|
-
|
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
|
184
|
-
|
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
|
188
|
-
|
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
|
204
|
-
|
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
|
232
|
-
|
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
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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(
|
257
|
-
[
|
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
|
275
|
-
self.tasks
|
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
|
-
|
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.
|
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.
|
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(
|
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 =
|
14
|
+
job = configure(job_config, options)
|
15
15
|
options[:interactive] = false
|
16
16
|
job.execute options
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
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
|
data/lib/libis/workflow.rb
CHANGED
@@ -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
|
|