rukawa 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/lib/rukawa/abstract_job.rb +17 -11
- data/lib/rukawa/cli.rb +59 -14
- data/lib/rukawa/configuration.rb +4 -1
- data/lib/rukawa/dependency.rb +59 -0
- data/lib/rukawa/errors.rb +1 -1
- data/lib/rukawa/job.rb +56 -5
- data/lib/rukawa/overview.rb +58 -0
- data/lib/rukawa/runner.rb +4 -25
- data/lib/rukawa/state.rb +12 -0
- data/lib/rukawa/version.rb +1 -1
- data/lib/rukawa.rb +3 -1
- data/rukawa.gemspec +2 -0
- data/sample/jobs/sample_job.rb +11 -0
- data/sample/result.dot +4 -4
- data/sample/result.png +0 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13b76e6a6b3bab23573723fee0d2c9049d1b5052
|
4
|
+
data.tar.gz: f39e1766871bd26e710edd2868edf39c625f1a0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9037f65f1a2c502a52cd53b3abc2758f2dcbb9deb43595f67687325c09924a298da2a7b79505a34bed2638128a278a8c5997efe07c4c4ee1884fb8f70620e19c
|
7
|
+
data.tar.gz: 42bb250a6a17de86a9d23f2343352d26426185a176313fdef7befcd4117aa4bac846be2d93393fac3e7970d1d18b115c2dadd87936a0512cb59ace0675b91d28
|
data/README.md
CHANGED
@@ -227,6 +227,21 @@ Main usage is manual reentering.
|
|
227
227
|
% dot -Tpng -o SampleJobNet.png SampleJobNet.dot
|
228
228
|
```
|
229
229
|
|
230
|
+
### Config Example
|
231
|
+
|
232
|
+
```
|
233
|
+
# rukawa.rb
|
234
|
+
|
235
|
+
Rukawa.configure do |c|
|
236
|
+
c.logger = OtherLogger.new
|
237
|
+
c.concurrency = 4
|
238
|
+
c.graph.concentrate = true
|
239
|
+
c.graph.nodesep = 0.8
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
see. [Rukawa::Configuration](https://github.com/joker1007/rukawa/blob/master/lib/rukawa/configuration.rb)
|
244
|
+
|
230
245
|
### help
|
231
246
|
```
|
232
247
|
% bundle exec rukawa help run
|
data/lib/rukawa/abstract_job.rb
CHANGED
@@ -1,18 +1,28 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'rukawa/state'
|
3
|
+
require 'active_support/core_ext/class'
|
3
4
|
|
4
5
|
module Rukawa
|
5
6
|
class AbstractJob
|
6
7
|
attr_reader :parent_job_net
|
8
|
+
extend ActiveSupport::DescendantsTracker
|
7
9
|
|
10
|
+
class_attribute :skip_rules, instance_writer: false
|
11
|
+
self.skip_rules = []
|
8
12
|
class << self
|
9
|
-
def
|
10
|
-
|
13
|
+
def add_skip_rule(callable_or_symbol)
|
14
|
+
self.skip_rules = skip_rules + [callable_or_symbol]
|
11
15
|
end
|
12
16
|
|
13
|
-
def
|
14
|
-
|
17
|
+
def description
|
18
|
+
@description
|
19
|
+
end
|
20
|
+
alias :desc :description
|
21
|
+
|
22
|
+
def set_description(body)
|
23
|
+
@description = body
|
15
24
|
end
|
25
|
+
alias :set_desc :set_description
|
16
26
|
end
|
17
27
|
|
18
28
|
def name
|
@@ -30,10 +40,6 @@ module Rukawa
|
|
30
40
|
end
|
31
41
|
end
|
32
42
|
|
33
|
-
def skip_rules
|
34
|
-
self.class.skip_rules
|
35
|
-
end
|
36
|
-
|
37
43
|
def elapsed_time_from(time = Time.now)
|
38
44
|
return finished_at - started_at if started_at && finished_at
|
39
45
|
return time - started_at if started_at
|
@@ -46,11 +52,11 @@ module Rukawa
|
|
46
52
|
return "N/A" unless elapsed
|
47
53
|
|
48
54
|
hour = elapsed.to_i / 3600
|
49
|
-
min = elapsed.to_i / 60
|
55
|
+
min = (elapsed - hour * 3600).to_i / 60
|
50
56
|
sec = (elapsed - hour * 3600 - min * 60).to_i
|
51
57
|
|
52
|
-
hour_format =
|
53
|
-
min_format = min > 0 ? "%dm " % min : ""
|
58
|
+
hour_format = hour > 0 ? "%dh " % hour : ""
|
59
|
+
min_format = hour > 0 || min > 0 ? "%dm " % min : ""
|
54
60
|
sec_format = "#{sec}s"
|
55
61
|
"#{hour_format}#{min_format}#{sec_format}"
|
56
62
|
end
|
data/lib/rukawa/cli.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'rukawa/runner'
|
3
|
+
require 'rukawa/overview'
|
3
4
|
|
4
5
|
module Rukawa
|
5
6
|
class Cli < Thor
|
@@ -10,20 +11,19 @@ module Rukawa
|
|
10
11
|
method_option :config, type: :string, default: nil, desc: "If this options is not set, try to load ./rukawa.rb"
|
11
12
|
method_option :job_dirs, type: :array, default: [], desc: "Load job directories"
|
12
13
|
method_option :batch, aliases: "-b", type: :boolean, default: false, desc: "If batch mode, not display running status"
|
13
|
-
method_option :log, aliases: "-l", type: :string,
|
14
|
+
method_option :log, aliases: "-l", type: :string, desc: "Default: ./rukawa.log"
|
14
15
|
method_option :stdout, type: :boolean, default: false, desc: "Output log to stdout"
|
16
|
+
method_option :syslog, type: :boolean, default: false, desc: "Output log to syslog"
|
15
17
|
method_option :dot, aliases: "-d", type: :string, default: nil, desc: "Output job status by dot format"
|
16
18
|
method_option :refresh_interval, aliases: "-r", type: :numeric, default: 3, desc: "Refresh interval for running status information"
|
17
19
|
def _run(job_net_name, *job_name)
|
18
20
|
load_config
|
19
|
-
|
20
|
-
|
21
|
-
c.concurrency = options[:concurrency] if options[:concurrency]
|
22
|
-
end
|
21
|
+
set_logger
|
22
|
+
set_concurrency
|
23
23
|
load_job_definitions
|
24
24
|
|
25
|
-
job_net_class =
|
26
|
-
job_classes = job_name.map { |name|
|
25
|
+
job_net_class = get_class(job_net_name)
|
26
|
+
job_classes = job_name.map { |name| get_class(name) }
|
27
27
|
job_net = job_net_class.new(nil, *job_classes)
|
28
28
|
result = Runner.run(job_net, options[:batch], options[:refresh_interval])
|
29
29
|
|
@@ -42,8 +42,8 @@ module Rukawa
|
|
42
42
|
load_config
|
43
43
|
load_job_definitions
|
44
44
|
|
45
|
-
job_net_class =
|
46
|
-
job_classes = job_name.map { |name|
|
45
|
+
job_net_class = get_class(job_net_name)
|
46
|
+
job_classes = job_name.map { |name| get_class(name) }
|
47
47
|
job_net = job_net_class.new(nil, *job_classes)
|
48
48
|
job_net.output_dot(options[:output])
|
49
49
|
end
|
@@ -60,13 +60,11 @@ module Rukawa
|
|
60
60
|
method_option :refresh_interval, aliases: "-r", type: :numeric, default: 3, desc: "Refresh interval for running status information"
|
61
61
|
def run_job(*job_name)
|
62
62
|
load_config
|
63
|
-
|
64
|
-
|
65
|
-
c.concurrency = options[:concurrency] if options[:concurrency]
|
66
|
-
end
|
63
|
+
set_logger
|
64
|
+
set_concurrency
|
67
65
|
load_job_definitions
|
68
66
|
|
69
|
-
job_classes = job_name.map { |name|
|
67
|
+
job_classes = job_name.map { |name| get_class(name) }
|
70
68
|
job_net_class = anonymous_job_net_class(*job_classes)
|
71
69
|
job_net = job_net_class.new(nil)
|
72
70
|
result = Runner.run(job_net, options[:batch], options[:refresh_interval])
|
@@ -78,6 +76,25 @@ module Rukawa
|
|
78
76
|
exit 1 unless result
|
79
77
|
end
|
80
78
|
|
79
|
+
desc "list", "List JobNet"
|
80
|
+
method_option :config, type: :string, default: nil, desc: "If this options is not set, try to load ./rukawa.rb"
|
81
|
+
method_option :jobs, aliases: "-j", type: :boolean, desc: "Show jobs", default: false
|
82
|
+
method_option :job_dirs, type: :array, default: [], desc: "Load job directories"
|
83
|
+
def list
|
84
|
+
load_config
|
85
|
+
load_job_definitions
|
86
|
+
Rukawa::Overview.list_job_net(with_jobs: options[:jobs])
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "list_job", "List Job"
|
90
|
+
method_option :config, type: :string, default: nil, desc: "If this options is not set, try to load ./rukawa.rb"
|
91
|
+
method_option :job_dirs, type: :array, default: [], desc: "Load job directories"
|
92
|
+
def list_job
|
93
|
+
load_config
|
94
|
+
load_job_definitions
|
95
|
+
Rukawa::Overview.list_job
|
96
|
+
end
|
97
|
+
|
81
98
|
private
|
82
99
|
|
83
100
|
def load_config
|
@@ -88,6 +105,27 @@ module Rukawa
|
|
88
105
|
end
|
89
106
|
end
|
90
107
|
|
108
|
+
def set_logger
|
109
|
+
Rukawa.configure do |c|
|
110
|
+
if options[:stdout]
|
111
|
+
c.logger = Logger.new($stdout)
|
112
|
+
elsif options[:syslog]
|
113
|
+
require 'syslog/logger'
|
114
|
+
c.logger = Syslog::Logger.new('rukawa')
|
115
|
+
elsif options[:log]
|
116
|
+
c.logger = Logger.new(options[:log])
|
117
|
+
else
|
118
|
+
c.logger ||= Logger.new('./rukawa.log');
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def set_concurrency
|
124
|
+
Rukawa.configure do |c|
|
125
|
+
c.concurrency = options[:concurrency] if options[:concurrency]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
91
129
|
def default_config_file
|
92
130
|
"./rukawa.rb"
|
93
131
|
end
|
@@ -103,6 +141,13 @@ module Rukawa
|
|
103
141
|
end
|
104
142
|
end
|
105
143
|
|
144
|
+
def get_class(name)
|
145
|
+
Object.const_get(name)
|
146
|
+
rescue NameError
|
147
|
+
$stderr.puts("`#{name}` class is not found")
|
148
|
+
exit 1
|
149
|
+
end
|
150
|
+
|
106
151
|
def anonymous_job_net_class(*job_classes)
|
107
152
|
Class.new(JobNet) do
|
108
153
|
self.singleton_class.send(:define_method, :dependencies) do
|
data/lib/rukawa/configuration.rb
CHANGED
@@ -6,9 +6,12 @@ require 'concurrent'
|
|
6
6
|
module Rukawa
|
7
7
|
class Configuration < Delegator
|
8
8
|
include Singleton
|
9
|
+
attr_accessor :logger
|
9
10
|
|
10
11
|
def initialize
|
11
|
-
@config = OpenStruct.new(
|
12
|
+
@config = OpenStruct.new(
|
13
|
+
concurrency: Concurrent.processor_count
|
14
|
+
)
|
12
15
|
@config.graph = GraphConfig.new.tap { |c| c.rankdir = "LR" }
|
13
16
|
end
|
14
17
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rukawa
|
2
|
+
module Dependency
|
3
|
+
def self.get(name)
|
4
|
+
const_get(name.to_s.split("_").map(&:capitalize).join)
|
5
|
+
end
|
6
|
+
|
7
|
+
class Base
|
8
|
+
def initialize(*results)
|
9
|
+
@results = results
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class AllSuccess < Base
|
18
|
+
def resolve
|
19
|
+
@results.all? { |r| r && r.success? }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class AllDone < Base
|
24
|
+
def resolve
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class OneSuccess < Base
|
30
|
+
def resolve
|
31
|
+
@results.empty? || @results.any? { |r| r && r.success? }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class AllSuccessOrSkipped < Base
|
36
|
+
def resolve
|
37
|
+
@results.all? { |r| r && (r.success? || r.skipped?) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class OneSuccessOrSkipped < Base
|
42
|
+
def resolve
|
43
|
+
@results.empty? || @results.any? { |r| r && (r.success? || r.skipped?) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class AllFailed < Base
|
48
|
+
def resolve
|
49
|
+
@results.none? { |r| r }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class OneFailed < Base
|
54
|
+
def resolve
|
55
|
+
@results.empty? || @results.any? { |r| r.nil? }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/rukawa/errors.rb
CHANGED
data/lib/rukawa/job.rb
CHANGED
@@ -1,15 +1,37 @@
|
|
1
1
|
require 'concurrent'
|
2
2
|
require 'rukawa/abstract_job'
|
3
|
+
require 'rukawa/dependency'
|
4
|
+
require 'rukawa/state'
|
5
|
+
require 'active_support/core_ext/class'
|
3
6
|
|
4
7
|
module Rukawa
|
5
8
|
class Job < AbstractJob
|
6
9
|
attr_accessor :in_comings, :out_goings
|
7
10
|
attr_reader :state, :started_at, :finished_at
|
8
11
|
|
12
|
+
class_attribute :retryable, :retry_limit, :retry_exception_type, :retry_wait, instance_writer: false
|
13
|
+
class_attribute :dependency_type, instance_writer: false
|
14
|
+
self.dependency_type = Dependency::AllSuccess
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def set_retryable(limit: 8, type: nil, wait: nil)
|
18
|
+
self.retryable = true
|
19
|
+
self.retry_limit = limit
|
20
|
+
self.retry_exception_type = type
|
21
|
+
self.retry_wait = wait
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_dependency_type(name)
|
25
|
+
self.dependency_type = Rukawa::Dependency.get(name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
9
29
|
def initialize(parent_job_net)
|
10
30
|
@parent_job_net = parent_job_net
|
11
31
|
@in_comings = Set.new
|
12
32
|
@out_goings = Set.new
|
33
|
+
@retry_count = 0
|
34
|
+
@retry_wait = 1
|
13
35
|
set_state(:waiting)
|
14
36
|
end
|
15
37
|
|
@@ -42,7 +64,7 @@ module Rukawa
|
|
42
64
|
@started_at = Time.now
|
43
65
|
check_dependencies(results)
|
44
66
|
|
45
|
-
if skip?
|
67
|
+
if skip?
|
46
68
|
Rukawa.logger.info("Skip #{self.class}")
|
47
69
|
set_state(:skipped)
|
48
70
|
else
|
@@ -54,7 +76,8 @@ module Rukawa
|
|
54
76
|
end
|
55
77
|
rescue => e
|
56
78
|
handle_error(e)
|
57
|
-
|
79
|
+
Rukawa.logger.error("Retry #{self.class}")
|
80
|
+
retry
|
58
81
|
ensure
|
59
82
|
@finished_at = Time.now
|
60
83
|
end
|
@@ -85,16 +108,44 @@ module Rukawa
|
|
85
108
|
end
|
86
109
|
end
|
87
110
|
|
111
|
+
def dependency_type
|
112
|
+
self.class.dependency_type
|
113
|
+
end
|
114
|
+
|
88
115
|
def check_dependencies(results)
|
89
|
-
|
116
|
+
dependency = dependency_type.new(*results)
|
117
|
+
unless dependency.resolve
|
90
118
|
set_state(:aborted)
|
91
|
-
raise
|
119
|
+
raise DependencyUnsatisfied
|
92
120
|
end
|
93
121
|
end
|
94
122
|
|
95
123
|
def handle_error(e)
|
96
124
|
Rukawa.logger.error("Error #{self.class} by #{e}")
|
97
|
-
|
125
|
+
if retry?(e)
|
126
|
+
@retry_count += 1
|
127
|
+
set_state(:waiting)
|
128
|
+
sleep @retry_wait
|
129
|
+
@retry_wait = self.class.retry_wait ? self.class.retry_wait : @retry_wait * 2
|
130
|
+
else
|
131
|
+
set_state(:error) unless e.is_a?(DependencyUnsatisfied)
|
132
|
+
raise e
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def retry?(e)
|
137
|
+
return false unless self.class.retryable
|
138
|
+
|
139
|
+
type_condition = case self.class.retry_exception_type
|
140
|
+
when Array
|
141
|
+
self.class.retry_exception_type.include?(e.class)
|
142
|
+
when Class
|
143
|
+
e.is_a?(self.class.retry_exception_type)
|
144
|
+
when nil
|
145
|
+
!e.is_a?(DependencyUnsatisfied)
|
146
|
+
end
|
147
|
+
|
148
|
+
type_condition && (self.class.retry_limit.nil? || self.class.retry_limit == 0 || @retry_count < self.class.retry_limit)
|
98
149
|
end
|
99
150
|
|
100
151
|
def store(key, value)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Rukawa
|
2
|
+
module Overview
|
3
|
+
class << self
|
4
|
+
def list_job_net(with_jobs: false)
|
5
|
+
header = ["Job", "Desc"]
|
6
|
+
header << "Dependencies" if with_jobs
|
7
|
+
table = Terminal::Table.new headings: header do |t|
|
8
|
+
JobNet.descendants.each do |job_net|
|
9
|
+
list_table_row(t, job_net, job_net.dependencies, with_jobs: with_jobs)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
puts table
|
13
|
+
end
|
14
|
+
|
15
|
+
def list_job
|
16
|
+
header = ["Job", "Desc"]
|
17
|
+
table = Terminal::Table.new headings: header do |t|
|
18
|
+
Job.descendants.each do |job|
|
19
|
+
row = [Paint[job.name, :bold, :underline], job.desc]
|
20
|
+
t << row
|
21
|
+
end
|
22
|
+
end
|
23
|
+
puts table
|
24
|
+
end
|
25
|
+
|
26
|
+
def list_table_row(table, job_net, level = 0, with_jobs: false)
|
27
|
+
row = [Paint[job_net.name, :bold, :underline], job_net.desc]
|
28
|
+
row << "" if with_jobs
|
29
|
+
table << row
|
30
|
+
if with_jobs
|
31
|
+
job_net.dependencies.each do |inner_j, deps|
|
32
|
+
table << [Paint["#{" "}#{inner_j.name}"], inner_j.desc, deps.join(", ")]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_running_status(root_job_net)
|
38
|
+
table = Terminal::Table.new headings: ["Job", "Status", "Elapsed Time"] do |t|
|
39
|
+
root_job_net.each_with_index do |j|
|
40
|
+
running_table_row(t, j)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
puts table
|
44
|
+
end
|
45
|
+
|
46
|
+
def running_table_row(table, job, level = 0)
|
47
|
+
if job.is_a?(JobNet)
|
48
|
+
table << [Paint["#{" " * level}#{job.class}", :bold, :underline], Paint[job.state.colored, :bold, :underline], Paint[job.formatted_elapsed_time_from, :bold, :underline]]
|
49
|
+
job.each do |inner_j|
|
50
|
+
running_table_row(table, inner_j, level + 1)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
table << [Paint["#{" " * level}#{job.class}", :bold], job.state.colored, job.formatted_elapsed_time_from]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/rukawa/runner.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'terminal-table'
|
2
2
|
require 'paint'
|
3
|
+
require 'rukawa/overview'
|
3
4
|
|
4
5
|
module Rukawa
|
5
6
|
class Runner
|
@@ -18,19 +19,19 @@ module Rukawa
|
|
18
19
|
Rukawa.logger.info("=== Start Rukawa ===")
|
19
20
|
futures = @root_job_net.dataflows.each(&:execute)
|
20
21
|
until futures.all?(&:complete?)
|
21
|
-
|
22
|
+
Overview.display_running_status(@root_job_net) unless batch_mode
|
22
23
|
sleep refresh_interval
|
23
24
|
end
|
24
25
|
Rukawa.logger.info("=== Finish Rukawa ===")
|
25
26
|
|
26
|
-
|
27
|
+
Overview.display_running_status(@root_job_net) unless batch_mode
|
27
28
|
puts "Finished #{@root_job_net.name} in #{@root_job_net.formatted_elapsed_time_from}"
|
28
29
|
|
29
30
|
errors = futures.map(&:reason).compact
|
30
31
|
|
31
32
|
unless errors.empty?
|
32
33
|
errors.each do |err|
|
33
|
-
next if err.is_a?(
|
34
|
+
next if err.is_a?(DependencyUnsatisfied)
|
34
35
|
Rukawa.logger.error(err)
|
35
36
|
end
|
36
37
|
return false
|
@@ -38,27 +39,5 @@ module Rukawa
|
|
38
39
|
|
39
40
|
true
|
40
41
|
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def display_table
|
45
|
-
table = Terminal::Table.new headings: ["Job", "Status", "Elapsed Time"] do |t|
|
46
|
-
@root_job_net.each_with_index do |j|
|
47
|
-
table_row(t, j)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
puts table
|
51
|
-
end
|
52
|
-
|
53
|
-
def table_row(table, job, level = 0)
|
54
|
-
if job.is_a?(JobNet)
|
55
|
-
table << [Paint["#{" " * level}#{job.class}", :bold, :underline], Paint[job.state.colored, :bold, :underline], Paint[job.formatted_elapsed_time_from, :bold, :underline]]
|
56
|
-
job.each do |inner_j|
|
57
|
-
table_row(table, inner_j, level + 1)
|
58
|
-
end
|
59
|
-
else
|
60
|
-
table << [Paint["#{" " * level}#{job.class}", :bold], job.state.colored, job.formatted_elapsed_time_from]
|
61
|
-
end
|
62
|
-
end
|
63
42
|
end
|
64
43
|
end
|
data/lib/rukawa/state.rb
CHANGED
@@ -16,6 +16,10 @@ module Rukawa::State
|
|
16
16
|
other
|
17
17
|
end
|
18
18
|
|
19
|
+
def success?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
19
23
|
%i(running? skipped? bypassed? error? aborted? waiting? finished?).each do |sym|
|
20
24
|
define_method(sym) do
|
21
25
|
false
|
@@ -66,6 +70,10 @@ module Rukawa::State
|
|
66
70
|
:yellow
|
67
71
|
end
|
68
72
|
|
73
|
+
def self.success?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
69
77
|
def self.bypassed?
|
70
78
|
true
|
71
79
|
end
|
@@ -138,6 +146,10 @@ module Rukawa::State
|
|
138
146
|
:green
|
139
147
|
end
|
140
148
|
|
149
|
+
def self.success?
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
141
153
|
def self.finished?
|
142
154
|
true
|
143
155
|
end
|
data/lib/rukawa/version.rb
CHANGED
data/lib/rukawa.rb
CHANGED
@@ -3,7 +3,7 @@ require "concurrent"
|
|
3
3
|
module Rukawa
|
4
4
|
class << self
|
5
5
|
def logger
|
6
|
-
|
6
|
+
config.logger
|
7
7
|
end
|
8
8
|
|
9
9
|
def store
|
@@ -24,9 +24,11 @@ module Rukawa
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
require 'active_support'
|
27
28
|
require "rukawa/version"
|
28
29
|
require 'rukawa/errors'
|
29
30
|
require 'rukawa/state'
|
31
|
+
require 'rukawa/dependency'
|
30
32
|
require 'rukawa/configuration'
|
31
33
|
require 'rukawa/job_net'
|
32
34
|
require 'rukawa/job'
|
data/rukawa.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_runtime_dependency "activesupport", ">= 4"
|
21
22
|
spec.add_runtime_dependency "concurrent-ruby"
|
22
23
|
spec.add_runtime_dependency "thor"
|
23
24
|
spec.add_runtime_dependency "terminal-table"
|
@@ -27,4 +28,5 @@ Gem::Specification.new do |spec|
|
|
27
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
28
29
|
spec.add_development_dependency "rspec", "~> 3.0"
|
29
30
|
spec.add_development_dependency "rspec-power_assert"
|
31
|
+
spec.add_development_dependency "rspec-parameterized"
|
30
32
|
end
|
data/sample/jobs/sample_job.rb
CHANGED
@@ -12,19 +12,29 @@ class SampleJob < Rukawa::Job
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class Job1 < SampleJob
|
15
|
+
set_description "Job1 description body"
|
15
16
|
end
|
16
17
|
class Job2 < SampleJob
|
18
|
+
def run
|
19
|
+
raise "job2 error"
|
20
|
+
end
|
17
21
|
end
|
18
22
|
class Job3 < SampleJob
|
19
23
|
end
|
20
24
|
class Job4 < SampleJob
|
25
|
+
# inherited by subclass
|
26
|
+
set_dependency_type :one_success
|
21
27
|
end
|
22
28
|
class Job5 < SampleJob
|
29
|
+
# inherited by subclass
|
30
|
+
set_retryable limit: 3, wait: 2, type: RuntimeError
|
31
|
+
|
23
32
|
def run
|
24
33
|
raise "job5 error"
|
25
34
|
end
|
26
35
|
end
|
27
36
|
class Job6 < SampleJob
|
37
|
+
set_dependency_type :one_failed
|
28
38
|
end
|
29
39
|
class Job7 < SampleJob
|
30
40
|
end
|
@@ -47,6 +57,7 @@ class InnerJob4 < SampleJob
|
|
47
57
|
end
|
48
58
|
|
49
59
|
class InnerJob5 < SampleJob
|
60
|
+
# inherited by subclass
|
50
61
|
add_skip_rule ->(job) { job.is_a?(SampleJob) }
|
51
62
|
end
|
52
63
|
|
data/sample/result.dot
CHANGED
@@ -2,7 +2,7 @@ digraph "SampleJobNet" {
|
|
2
2
|
label = "SampleJobNet";
|
3
3
|
graph [rankdir = LR,nodesep = 0.8,concentrate = true];
|
4
4
|
Job1 [style = filled,fillcolor = green];
|
5
|
-
Job2 [style = filled,fillcolor =
|
5
|
+
Job2 [style = filled,fillcolor = red];
|
6
6
|
Job3 [style = filled,fillcolor = green];
|
7
7
|
Job4 [style = filled,fillcolor = green];
|
8
8
|
subgraph "cluster_InnerJobNet" {
|
@@ -17,15 +17,15 @@ InnerJob2 [style = filled,fillcolor = red];
|
|
17
17
|
}
|
18
18
|
Job8 [style = filled,fillcolor = magenta];
|
19
19
|
Job5 [style = filled,fillcolor = red];
|
20
|
-
Job6 [style = filled,fillcolor =
|
21
|
-
Job7 [style = filled,fillcolor =
|
20
|
+
Job6 [style = filled,fillcolor = green];
|
21
|
+
Job7 [style = filled,fillcolor = green];
|
22
22
|
subgraph "cluster_InnerJobNet2" {
|
23
23
|
label = "InnerJobNet2";
|
24
24
|
graph [rankdir = LR,nodesep = 0.8,concentrate = true];
|
25
25
|
color = blue;
|
26
26
|
InnerJob4 [style = filled,fillcolor = green];
|
27
27
|
InnerJob5 [style = filled,fillcolor = yellow];
|
28
|
-
InnerJob6 [style = filled,fillcolor =
|
28
|
+
InnerJob6 [style = filled,fillcolor = magenta];
|
29
29
|
"InnerJob4" -> "InnerJob5";
|
30
30
|
"InnerJob4" -> "InnerJob6";
|
31
31
|
"InnerJob5" -> "InnerJob6";
|
data/sample/result.png
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rukawa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- joker1007
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: concurrent-ruby
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +136,20 @@ dependencies:
|
|
122
136
|
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
124
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec-parameterized
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
125
153
|
description: Hyper simple job workflow engine
|
126
154
|
email:
|
127
155
|
- kakyoin.hierophant@gmail.com
|
@@ -144,9 +172,11 @@ files:
|
|
144
172
|
- lib/rukawa/cli.rb
|
145
173
|
- lib/rukawa/configuration.rb
|
146
174
|
- lib/rukawa/dag.rb
|
175
|
+
- lib/rukawa/dependency.rb
|
147
176
|
- lib/rukawa/errors.rb
|
148
177
|
- lib/rukawa/job.rb
|
149
178
|
- lib/rukawa/job_net.rb
|
179
|
+
- lib/rukawa/overview.rb
|
150
180
|
- lib/rukawa/runner.rb
|
151
181
|
- lib/rukawa/state.rb
|
152
182
|
- lib/rukawa/version.rb
|