tengine_job 0.6.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +109 -0
- data/README.rdoc +20 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/examples/0004_retry_one_layer.rb +24 -0
- data/examples/0004_retry_one_layer.sh +38 -0
- data/examples/0005_retry_two_layer.rb +54 -0
- data/examples/0005_retry_two_layer.sh +80 -0
- data/examples/0006_retry_three_layer.rb +58 -0
- data/examples/0006_retry_three_layer.sh +74 -0
- data/examples/0007_simple_jobnet.rb +7 -0
- data/examples/0021_dynamic_env.rb +20 -0
- data/examples/VERSION +1 -0
- data/examples/tengine_job_test.sh +10 -0
- data/lib/tengine/job.rb +94 -0
- data/lib/tengine/job/category.rb +54 -0
- data/lib/tengine/job/connectable.rb +43 -0
- data/lib/tengine/job/drivers/job_control_driver.rb +82 -0
- data/lib/tengine/job/drivers/job_execution_driver.rb +30 -0
- data/lib/tengine/job/drivers/jobnet_control_driver.rb +117 -0
- data/lib/tengine/job/drivers/schedule_driver.rb +30 -0
- data/lib/tengine/job/dsl_binder.rb +12 -0
- data/lib/tengine/job/dsl_evaluator.rb +18 -0
- data/lib/tengine/job/dsl_loader.rb +180 -0
- data/lib/tengine/job/edge.rb +150 -0
- data/lib/tengine/job/element_selector_notation.rb +169 -0
- data/lib/tengine/job/end.rb +32 -0
- data/lib/tengine/job/executable.rb +74 -0
- data/lib/tengine/job/execution.rb +141 -0
- data/lib/tengine/job/expansion.rb +37 -0
- data/lib/tengine/job/fork.rb +6 -0
- data/lib/tengine/job/job.rb +23 -0
- data/lib/tengine/job/jobnet.rb +173 -0
- data/lib/tengine/job/jobnet/builder.rb +150 -0
- data/lib/tengine/job/jobnet/job_state_transition.rb +167 -0
- data/lib/tengine/job/jobnet/jobnet_state_transition.rb +110 -0
- data/lib/tengine/job/jobnet/state_transition.rb +37 -0
- data/lib/tengine/job/jobnet_actual.rb +55 -0
- data/lib/tengine/job/jobnet_template.rb +10 -0
- data/lib/tengine/job/join.rb +6 -0
- data/lib/tengine/job/junction.rb +29 -0
- data/lib/tengine/job/killing.rb +30 -0
- data/lib/tengine/job/mm_compatibility.rb +6 -0
- data/lib/tengine/job/mm_compatibility/connectable.rb +13 -0
- data/lib/tengine/job/name_path.rb +31 -0
- data/lib/tengine/job/root.rb +16 -0
- data/lib/tengine/job/root_jobnet_actual.rb +39 -0
- data/lib/tengine/job/root_jobnet_template.rb +49 -0
- data/lib/tengine/job/script_executable.rb +235 -0
- data/lib/tengine/job/signal.rb +121 -0
- data/lib/tengine/job/start.rb +20 -0
- data/lib/tengine/job/stoppable.rb +15 -0
- data/lib/tengine/job/vertex.rb +172 -0
- data/lib/tengine_job.rb +3 -0
- data/spec/fixtures/rjn_0001_simple_jobnet_builder.rb +42 -0
- data/spec/fixtures/rjn_0002_simple_parallel_jobnet_builder.rb +42 -0
- data/spec/fixtures/rjn_0003_fork_join_jobnet_builder.rb +61 -0
- data/spec/fixtures/rjn_0004_parallel_jobnet_with_finally_fixture.rb +62 -0
- data/spec/fixtures/rjn_0005_retry_two_layer_fixture.rb +153 -0
- data/spec/fixtures/rjn_0008_expansion_fixture.rb +32 -0
- data/spec/fixtures/rjn_0009_tree_sequential_jobnet_builder.rb +174 -0
- data/spec/fixtures/rjn_0010_2jobs_and_1job_parallel_jobnet_builder.rb +39 -0
- data/spec/fixtures/rjn_0011_nested_fork_jobnet_builder.rb +96 -0
- data/spec/fixtures/rjn_0012_nested_and_finally_builder.rb +157 -0
- data/spec/fixtures/rjn_1004_hadoop_job_in_jobnet_fixture.rb +105 -0
- data/spec/fixtures/rjn_means_root_jobnet +0 -0
- data/spec/fixtures/test_credential_fixture.rb +12 -0
- data/spec/fixtures/test_server_fixture.rb +28 -0
- data/spec/mongoid.yml +35 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/sshd/.gitignore +1 -0
- data/spec/sshd/id_rsa +51 -0
- data/spec/sshd/id_rsa.pub +1 -0
- data/spec/sshd/ssh_host_rsa_key +51 -0
- data/spec/sshd/ssh_host_rsa_key.pub +1 -0
- data/spec/sshd/sshd_config +10 -0
- data/spec/sshd/sshd_config.erb +11 -0
- data/spec/sshd/tengine_job_test.sh +6 -0
- data/spec/support/jobnet_fixture_builder.rb +145 -0
- data/spec/support/mongo_index_key_log.rb +91 -0
- data/spec/tengine/job/category_spec.rb +193 -0
- data/spec/tengine/job/connectable_spec.rb +94 -0
- data/spec/tengine/job/drivers/job_controll_driver/connection_error_spec.rb +236 -0
- data/spec/tengine/job/drivers/job_controll_driver/duplicated_job_start_spec.rb +302 -0
- data/spec/tengine/job/drivers/job_controll_driver/expansion_spec.rb +120 -0
- data/spec/tengine/job/drivers/job_controll_driver/stop_spec.rb +159 -0
- data/spec/tengine/job/drivers/job_controll_driver_spec.rb +623 -0
- data/spec/tengine/job/drivers/job_execution_driver_spec.rb +88 -0
- data/spec/tengine/job/drivers/jobnet_control_driver/nested_and_finally_spec.rb +472 -0
- data/spec/tengine/job/drivers/jobnet_control_driver/nested_jobnet_spec.rb +231 -0
- data/spec/tengine/job/drivers/jobnet_control_driver/stop_jobnet_spec.rb +202 -0
- data/spec/tengine/job/drivers/jobnet_control_driver_spec.rb +446 -0
- data/spec/tengine/job/drivers/schedule_driver_spec.rb +202 -0
- data/spec/tengine/job/dsl_binder_spec.rb +36 -0
- data/spec/tengine/job/dsl_loader_spec.rb +403 -0
- data/spec/tengine/job/dsls/0013_hadoop_job_run.rb +29 -0
- data/spec/tengine/job/dsls/0014_join_and_join.rb +19 -0
- data/spec/tengine/job/dsls/0015_fork_and_fork.rb +18 -0
- data/spec/tengine/job/dsls/0016_complex_fork_and_join.rb +20 -0
- data/spec/tengine/job/dsls/0017_finally.rb +15 -0
- data/spec/tengine/job/dsls/0018_expansion.rb +23 -0
- data/spec/tengine/job/dsls/0019_execute_job_on_event.rb +16 -0
- data/spec/tengine/job/dsls/0020_duplicated_jobnet_name.rb +16 -0
- data/spec/tengine/job/dsls/1060_test_dir1/1060_test_dir2/0013_hadoop_job_run.rb +29 -0
- data/spec/tengine/job/dsls/2003_expansion/expansion_5.rb +11 -0
- data/spec/tengine/job/dsls/VERSION +1 -0
- data/spec/tengine/job/dynamic_env_spec.rb +95 -0
- data/spec/tengine/job/edge_spec.rb +241 -0
- data/spec/tengine/job/element_selector_notation_spec.rb +354 -0
- data/spec/tengine/job/examples_spec.rb +62 -0
- data/spec/tengine/job/execution_spec.rb +100 -0
- data/spec/tengine/job/expansion_spec.rb +116 -0
- data/spec/tengine/job/hadoop_job_run_spec.rb +65 -0
- data/spec/tengine/job/job_spec.rb +4 -0
- data/spec/tengine/job/jobnet/1015_complecated_jobnet_spec.rb +72 -0
- data/spec/tengine/job/jobnet_actual_spec.rb +175 -0
- data/spec/tengine/job/jobnet_spec.rb +399 -0
- data/spec/tengine/job/jobnet_template_spec.rb +240 -0
- data/spec/tengine/job/killing_spec.rb +91 -0
- data/spec/tengine/job/reset_spec.rb +958 -0
- data/spec/tengine/job/reset_spec/4056_1_dump.txt +1 -0
- data/spec/tengine/job/root_jobnet_actual_spec.rb +89 -0
- data/spec/tengine/job/root_jobnet_template_spec.rb +248 -0
- data/spec/tengine/job/script_executable_spec.rb +132 -0
- data/spec/tengine/job/stoppable_spec.rb +176 -0
- data/spec/tengine/job/vertex_spec.rb +25 -0
- data/spec/tengine_job_spec.rb +4 -0
- data/tengine_job.gemspec +197 -0
- data/tmp/log/.gitignore +1 -0
- metadata +296 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# スケジュール管理ドライバ
|
4
|
+
driver :schedule_driver do
|
5
|
+
|
6
|
+
on :'start.execution.job.tengine' do
|
7
|
+
exec = Tengine::Job::Signal.new(event).execution
|
8
|
+
name = exec.name_as_resource
|
9
|
+
status = Tengine::Core::Schedule::SCHEDULED
|
10
|
+
if exec.actual_base_timeout_alert && !exec.actual_base_timeout_alert.zero?
|
11
|
+
t1 = Time.now + (exec.actual_base_timeout_alert * 60.0)
|
12
|
+
Tengine::Core::Schedule.create event_type_name: "alert.execution.job.tengine", scheduled_at: t1, source_name: name, status: status , properties: event.properties
|
13
|
+
end
|
14
|
+
if exec.actual_base_timeout_termination && !exec.actual_base_timeout_termination.zero?
|
15
|
+
t2 = Time.now + (exec.actual_base_timeout_termination * 60.0)
|
16
|
+
Tengine::Core::Schedule.create event_type_name: "stop.execution.job.tengine", scheduled_at: t2, source_name: name, status: status, properties: event.properties.merge(stop_reason: 'timeout')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
on :'success.execution.job.tengine' do
|
21
|
+
name = Tengine::Job::Signal.new(event).execution.name_as_resource
|
22
|
+
Tengine::Core::Schedule.where(source_name: name, status: Tengine::Core::Schedule::SCHEDULED).update_all(status: Tengine::Core::Schedule::INVALID)
|
23
|
+
end
|
24
|
+
|
25
|
+
on :'error.execution.job.tengine' do
|
26
|
+
name = Tengine::Job::Signal.new(event).execution.name_as_resource
|
27
|
+
Tengine::Core::Schedule.where(source_name: name, status: Tengine::Core::Schedule::SCHEDULED).update_all(status: Tengine::Core::Schedule::INVALID)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# ジョブDSLをロードする際に使用される語彙に関するメソッドを定義するモジュール
|
5
|
+
module Tengine::Job::DslBinder
|
6
|
+
include Tengine::Job::DslEvaluator
|
7
|
+
|
8
|
+
def jobnet(name, *args, &block)
|
9
|
+
Tengine::Job::RootJobnetTemplate.by_name(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# Tengine::Job::DslLoader と Tengine::Job::DslBinder がincludeしているモジュールです。
|
5
|
+
# それぞれに共通のメソッドを定義します。
|
6
|
+
module Tengine::Job::DslEvaluator
|
7
|
+
private
|
8
|
+
def __stack_instance_variable__(ivar_name, value)
|
9
|
+
backup = instance_variable_get(ivar_name)
|
10
|
+
instance_variable_set(ivar_name, value)
|
11
|
+
begin
|
12
|
+
return yield if block_given?
|
13
|
+
ensure
|
14
|
+
instance_variable_set(ivar_name, backup)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# ジョブDSLをロードする際に使用される語彙に関するメソッドを定義するモジュール
|
5
|
+
module Tengine::Job::DslLoader
|
6
|
+
include Tengine::Job::DslEvaluator
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def loading_template_block_store
|
10
|
+
@loading_template_block_store ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def template_block_store
|
14
|
+
@template_block_store ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def template_block_store_key(job, name)
|
18
|
+
"#{job.root.id.to_s}/#{job.id.to_s}##{name}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_loaded_blocks(loaded_root)
|
22
|
+
if loaded_root
|
23
|
+
loading_template_block_store.each do |unsaved_job, (name, block)|
|
24
|
+
loaded_job = loaded_root.vertex_by_name_path(unsaved_job.name_path)
|
25
|
+
key = template_block_store_key(loaded_job, name)
|
26
|
+
template_block_store[key] = block
|
27
|
+
end
|
28
|
+
else
|
29
|
+
loading_template_block_store.each do |saved_job, (name, block)|
|
30
|
+
key = template_block_store_key(saved_job, name)
|
31
|
+
template_block_store[key] = block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
loading_template_block_store.clear
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def jobnet(name, *args, &block)
|
40
|
+
options = args.extract_options!
|
41
|
+
options = {
|
42
|
+
:name => name,
|
43
|
+
:description => args.first || name,
|
44
|
+
}.update(options)
|
45
|
+
auto_sequence = options.delete(:auto_sequence)
|
46
|
+
result = __with_redirection__(options) do
|
47
|
+
if @jobnet.nil?
|
48
|
+
klass = Tengine::Job::RootJobnetTemplate
|
49
|
+
options[:dsl_version] = config.dsl_version
|
50
|
+
path, lineno = *block.source_location
|
51
|
+
options[:dsl_filepath] = config.relative_path_from_dsl_dir(path)
|
52
|
+
options[:dsl_lineno] = lineno.to_i
|
53
|
+
else
|
54
|
+
klass = Tengine::Job::JobnetTemplate
|
55
|
+
end
|
56
|
+
klass.new(options)
|
57
|
+
end
|
58
|
+
result.with_start
|
59
|
+
@jobnet.children << result if @jobnet
|
60
|
+
if result.parent.nil?
|
61
|
+
if duplicated = result.find_duplication
|
62
|
+
if (duplicated.dsl_filepath != result.dsl_filepath) ||
|
63
|
+
(duplicated.dsl_lineno != result.dsl_lineno)
|
64
|
+
raise Tengine::Job::DslError, "2 jobnet named #{name.inspect} found at #{duplicated.dsl_filepath}:#{duplicated.dsl_lineno} and #{result.dsl_filepath}:#{result.dsl_lineno}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
__stack_instance_variable__(:@auto_sequence, auto_sequence || @auto_sequence) do
|
70
|
+
__stack_instance_variable__(:@boot_job_names, []) do
|
71
|
+
__stack_instance_variable__(:@redirections, []) do
|
72
|
+
__stack_instance_variable__(:@jobnet, result, &block)
|
73
|
+
result.build_edges(@auto_sequence, @boot_job_names, @redirections)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
if result.parent.nil?
|
78
|
+
loaded = result.find_duplication
|
79
|
+
result.save! unless loaded
|
80
|
+
Tengine::Job::DslLoader.update_loaded_blocks(loaded)
|
81
|
+
loaded || result
|
82
|
+
else
|
83
|
+
result
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def auto_sequence
|
88
|
+
@auto_sequence = true
|
89
|
+
end
|
90
|
+
|
91
|
+
def boot_jobs(*boot_job_names)
|
92
|
+
@auto_sequence = false
|
93
|
+
@boot_job_names = boot_job_names
|
94
|
+
end
|
95
|
+
|
96
|
+
def job(name, *args)
|
97
|
+
script, description, options = __parse_job_args__(name, args)
|
98
|
+
options = {
|
99
|
+
:name => name,
|
100
|
+
:description => description,
|
101
|
+
:script => script
|
102
|
+
}.update(options)
|
103
|
+
preparation = options.delete(:preparation)
|
104
|
+
result = __with_redirection__(options) do
|
105
|
+
Tengine::Job::JobnetTemplate.new(options)
|
106
|
+
end
|
107
|
+
@jobnet.children << result
|
108
|
+
if preparation
|
109
|
+
Tengine::Job::DslLoader.loading_template_block_store[result] = [:preparation, preparation]
|
110
|
+
end
|
111
|
+
result
|
112
|
+
end
|
113
|
+
|
114
|
+
def hadoop_job_run(name, *args, &block)
|
115
|
+
script, description, options = __parse_job_args__(name, args)
|
116
|
+
options[:script] = script
|
117
|
+
options[:jobnet_type_key] = :hadoop_job_run
|
118
|
+
jobnet(name, description, options, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
def hadoop_job(name, options = {})
|
122
|
+
result = __with_redirection__(options) do
|
123
|
+
Tengine::Job::JobnetTemplate.new(:name => name, :jobnet_type_key => :hadoop_job)
|
124
|
+
end
|
125
|
+
result.children << start = Tengine::Job::Start.new
|
126
|
+
result.children << fork = Tengine::Job::Fork.new
|
127
|
+
result.children << map = Tengine::Job::JobnetTemplate.new(:name => "Map" , :jobnet_type_key => :map_phase )
|
128
|
+
result.children << reduce = Tengine::Job::JobnetTemplate.new(:name => "Reduce", :jobnet_type_key => :reduce_phase)
|
129
|
+
result.children << join = Tengine::Job::Join.new
|
130
|
+
result.children << _end = Tengine::Job::End.new
|
131
|
+
result.edges.new(:origin_id => start.id , :destination_id => fork.id )
|
132
|
+
result.edges.new(:origin_id => fork.id , :destination_id => map.id )
|
133
|
+
result.edges.new(:origin_id => fork.id , :destination_id => reduce.id)
|
134
|
+
result.edges.new(:origin_id => map.id , :destination_id => join.id )
|
135
|
+
result.edges.new(:origin_id => reduce.id, :destination_id => join.id )
|
136
|
+
result.edges.new(:origin_id => join.id , :destination_id => _end.id )
|
137
|
+
@jobnet.children << result
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
def finally(&block)
|
142
|
+
jobnet("finally", :jobnet_type_key => :finally, &block)
|
143
|
+
end
|
144
|
+
|
145
|
+
def expansion(root_jobnet_name, options = {})
|
146
|
+
options = {
|
147
|
+
:name => root_jobnet_name,
|
148
|
+
}.update(options)
|
149
|
+
result = __with_redirection__(options) do
|
150
|
+
Tengine::Job::Expansion.new(options)
|
151
|
+
end
|
152
|
+
@jobnet.children << result
|
153
|
+
result
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def __parse_job_args__(name, args)
|
158
|
+
options = args.extract_options!
|
159
|
+
description_or_script, script = *args
|
160
|
+
if script
|
161
|
+
description = description_or_script
|
162
|
+
else
|
163
|
+
script = description_or_script
|
164
|
+
description = name
|
165
|
+
end
|
166
|
+
return script, description, options
|
167
|
+
end
|
168
|
+
|
169
|
+
def __with_redirection__(options)
|
170
|
+
destination_names = Array(options.delete(:to) || options.delete(:redirect_to))
|
171
|
+
result = yield
|
172
|
+
destination_names.each do |dest_name|
|
173
|
+
@redirections << [result.name, dest_name]
|
174
|
+
end
|
175
|
+
result
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
|
180
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
require 'selectable_attr'
|
4
|
+
|
5
|
+
# Vertexとともにジョブネットを構成するグラフの「辺」を表すモデル
|
6
|
+
# Tengine::Job::Jobnetにembeddedされます。
|
7
|
+
class Tengine::Job::Edge
|
8
|
+
include Mongoid::Document
|
9
|
+
include Mongoid::Timestamps
|
10
|
+
include Tengine::Core::SelectableAttr
|
11
|
+
include Tengine::Job::Signal::Transmittable
|
12
|
+
|
13
|
+
class StatusError < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
embedded_in :owner, :class_name => "Tengine::Job::Jobnet", :inverse_of => :edges
|
17
|
+
|
18
|
+
field :phase_cd , :type => Integer, :default => 0 # ステータス。とりうる値は後述を参照してください。詳しくはtengine_jobパッケージ設計書の「edge状態遷移」を参照してください。
|
19
|
+
field :origin_id , :type => BSON::ObjectId # 辺の遷移元となるvertexのid
|
20
|
+
field :destination_id, :type => BSON::ObjectId # 辺の遷移先となるvertexのid
|
21
|
+
|
22
|
+
validates :origin_id, :presence => true
|
23
|
+
validates :destination_id, :presence => true
|
24
|
+
|
25
|
+
selectable_attr :phase_cd do
|
26
|
+
entry 0, :active , "active" , :alive => true
|
27
|
+
entry 10, :transmitting, "transmitting", :alive => true
|
28
|
+
entry 20, :transmitted , "transmitted" , :alive => false
|
29
|
+
entry 30, :suspended , "suspended" , :alive => true
|
30
|
+
entry 31, :keeping , "keeping" , :alive => true
|
31
|
+
entry 40, :closing , "closing" , :alive => false
|
32
|
+
entry 50, :closed , "closed" , :alive => false
|
33
|
+
end
|
34
|
+
|
35
|
+
def alive?; !!phase_entry[:alive]; end
|
36
|
+
def alive_or_closing?; alive? || closing?; end
|
37
|
+
def alive_or_closing_or_closed?; alive? || closing? || closed?; end
|
38
|
+
|
39
|
+
phase_keys.each do |phase_key|
|
40
|
+
class_eval(<<-END_OF_METHOD)
|
41
|
+
def #{phase_key}?; phase_key == #{phase_key.inspect}; end
|
42
|
+
END_OF_METHOD
|
43
|
+
end
|
44
|
+
|
45
|
+
def origin
|
46
|
+
owner.children.detect{|c| c.id == origin_id}
|
47
|
+
end
|
48
|
+
|
49
|
+
def destination
|
50
|
+
owner.children.detect{|c| c.id == destination_id}
|
51
|
+
end
|
52
|
+
|
53
|
+
def name_for_message
|
54
|
+
"edge(#{id.to_s}) from #{origin ? origin.name_path : 'no origin'} to #{destination ? destination.name_path : 'no destination'}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def inspect
|
58
|
+
"#<#{self.class.name} #{name_for_message}>"
|
59
|
+
end
|
60
|
+
|
61
|
+
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#3E9EA
|
62
|
+
def transmit(signal)
|
63
|
+
case phase_key
|
64
|
+
when :active then
|
65
|
+
d = destination
|
66
|
+
if signal.execution.in_scope?(d)
|
67
|
+
self.phase_key = :transmitting
|
68
|
+
signal.leave(self)
|
69
|
+
else
|
70
|
+
Tengine.logger.info("#{d.name_path} will not be executed, becauase it is out of execution scope.")
|
71
|
+
end
|
72
|
+
when :suspended then
|
73
|
+
self.phase_key = :keeping
|
74
|
+
when :closing then
|
75
|
+
self.phase_key = :closed
|
76
|
+
signal.paths << self
|
77
|
+
signal.with_paths_backup do
|
78
|
+
if destination.is_a?(Tengine::Job::Job)
|
79
|
+
destination.next_edges.first.transmit(signal)
|
80
|
+
else
|
81
|
+
signal.leave(self)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def complete(signal)
|
88
|
+
case phase_key
|
89
|
+
when :transmitting then
|
90
|
+
self.phase_key = :transmitted
|
91
|
+
when :active, :closed then
|
92
|
+
# IG
|
93
|
+
when :suspended, :keeping then
|
94
|
+
# N/A
|
95
|
+
raise Tengine::Job::Edge::StatusError, "#{self.class.name}#complete not available on #{phase_key.inspect} at #{self.inspect}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def reset(signal)
|
100
|
+
# 全てのステータスから遷移する
|
101
|
+
if d = destination
|
102
|
+
if signal.execution.in_scope?(d)
|
103
|
+
self.phase_key = :active
|
104
|
+
d.reset(signal)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
raise "destination not found: #{destination_id.inspect} from #{origin.inspect}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def close(signal)
|
112
|
+
case phase_key
|
113
|
+
when :active, :suspended, :keeping, :transmitting then
|
114
|
+
self.phase_key = :closing
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def close_followings
|
119
|
+
accept_visitor(Tengine::Job::Edge::Closer.new)
|
120
|
+
end
|
121
|
+
|
122
|
+
def accept_visitor(visitor)
|
123
|
+
visitor.visit(self)
|
124
|
+
end
|
125
|
+
|
126
|
+
def phase_key=(phase_key)
|
127
|
+
Tengine.logger.debug("edge phase changed. <#{self.id.to_s}> #{self.phase_name} -> #{Tengine::Job::Edge.phase_name_by_key(phase_key)}")
|
128
|
+
self.write_attribute(:phase_cd, Tengine::Job::Edge.phase_id_by_key(phase_key))
|
129
|
+
end
|
130
|
+
|
131
|
+
class Closer
|
132
|
+
def visit(obj)
|
133
|
+
if obj.is_a?(Tengine::Job::End)
|
134
|
+
if parent = obj.parent
|
135
|
+
(parent.next_edges || []).each{|edge| edge.accept_visitor(self)}
|
136
|
+
end
|
137
|
+
elsif obj.is_a?(Tengine::Job::Vertex)
|
138
|
+
obj.next_edges.each{|edge| edge.accept_visitor(self)}
|
139
|
+
elsif obj.is_a?(Tengine::Job::Edge)
|
140
|
+
obj.close(nil)
|
141
|
+
obj.destination.accept_visitor(self)
|
142
|
+
else
|
143
|
+
raise "Unsupported class #{obj.inspect}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# # Tengine Jobnet Element Selector Notation
|
5
|
+
# Tengineジョブネット要素セレクタ記法
|
6
|
+
#
|
7
|
+
# Tengineジョブネット要素セレクタ記法は、TenigneのジョブDSLで生成されるジョブネットから
|
8
|
+
# 特定のVertexやEdgeを特定するための記述方法です。
|
9
|
+
# 正式名称が長いので、ここでは略して「セレクタ記法」と呼ぶ
|
10
|
+
#
|
11
|
+
# ## 背景
|
12
|
+
# Tengienジョブでは、ジョブやジョブネットなどのvertexとそれらを結ぶedgeが連携して動くこと
|
13
|
+
# ジョブの実行を行うが、ジョブ、ジョブネット以外のvertexとedgeには名前がついていない。
|
14
|
+
# しかし、ジョブネット内の要素群が連携して動作することを確認するためには、どのvertex、
|
15
|
+
# あるいはどのedgeなのかという点について状態などを確認する必要がある。
|
16
|
+
#
|
17
|
+
# ## name_path
|
18
|
+
# Tengineのジョブ/ジョブネットは親子関係によるツリー構造を持ち、
|
19
|
+
# それぞれ兄弟間では重複しない名前が指定されるので、スラッシュによって区切ることによって
|
20
|
+
# ファイルパスのような表記によって、どのジョブ/ジョブネットなのかを特定できる
|
21
|
+
#
|
22
|
+
# ## name_pathの問題点
|
23
|
+
# ジョブとジョブネットについては名前があるのでname_pathだけで特定できるが、
|
24
|
+
# それ以外のedgeやfork, joinについては特定することができない。
|
25
|
+
#
|
26
|
+
# ## それぞれの指定方法
|
27
|
+
# ### ジョブ/ジョブネット
|
28
|
+
# 1. name_pathをそのまま
|
29
|
+
# * 例: "/j100/j110/j111" # jxxx は、ジョブあるいはジョブネットを想定しています
|
30
|
+
# 2. "#{ジョブ/ジョブネット名}@#{ジョブネットのname_path}"
|
31
|
+
# * 例: "j111@/j100/j110"
|
32
|
+
#
|
33
|
+
#
|
34
|
+
# ### startとend
|
35
|
+
# 1. "#{start or end}!#{ジョブネットのセレクタ}"
|
36
|
+
# * 例: start!j110@/j100
|
37
|
+
# * 例: end!/j100/j110
|
38
|
+
# 2. "#{start or end}!#{ジョブネットのname_path}"
|
39
|
+
# * 例: start@/j100/j110
|
40
|
+
# * 例: end@/j100/j110
|
41
|
+
#
|
42
|
+
# ### edge
|
43
|
+
# #### ジョブ/ジョブネットの前後のedge
|
44
|
+
# 1. "#{prev or next}!#{ジョブ/ジョブネットのセレクタ}"
|
45
|
+
# * 例: prev!j110@/j100
|
46
|
+
# * 例: next!/j100/j110
|
47
|
+
#
|
48
|
+
# #### ジョブ/ジョブネットの間のedge
|
49
|
+
# 1. "#{前のジョブ名}~#{後のジョブ名}@#{ジョブネットのセレクタ}"
|
50
|
+
# * 例: j111~j112@/j100/j110
|
51
|
+
#
|
52
|
+
# #### fork-join間のedge
|
53
|
+
# 1. "fork~join!#{前のジョブ名}~#{後のジョブ名}@#{ジョブネットのセレクタ}"
|
54
|
+
# * 例: fork~join!j112~j113@/j100/j110
|
55
|
+
#
|
56
|
+
# ### fork, join
|
57
|
+
# 1. "#{fork or join}!#{前のジョブ名}~#{後のジョブ名}@#{ジョブネットのセレクタ}"
|
58
|
+
# * 例: fork!j112~j113@/j100/j110
|
59
|
+
# * 例: join!j112~j113@/j100/j110
|
60
|
+
#
|
61
|
+
#
|
62
|
+
|
63
|
+
module Tengine::Job::ElementSelectorNotation
|
64
|
+
|
65
|
+
class NotFound < Tengine::Errors::NotFound
|
66
|
+
attr_reader :jobnet, :notation
|
67
|
+
def initialize(jobnet, notation)
|
68
|
+
@jobnet, @notation = jobnet, notation
|
69
|
+
end
|
70
|
+
|
71
|
+
def message
|
72
|
+
"Tengine Jobnet Element not found by selector #{notation.inspect} in #{@jobnet.name_path}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
NAME_PART = /[A-Za-z_][\w\-]*/.freeze
|
78
|
+
NAME_PATH_PART = /[A-Za-z_\/][\w\-\/]*/.freeze
|
79
|
+
|
80
|
+
# elementメソッドは指定されたnotationによって対象となる要素が見つからなかった場合はnilを返します。
|
81
|
+
# 例外をraiseさせたい場合は element!メソッドを使ってください。
|
82
|
+
def element(notation)
|
83
|
+
direction, current_path = *notation.split(/@/, 2)
|
84
|
+
return vertex_by_name_path(direction) if current_path.nil? && Tengine::Job::NamePath.absolute?(direction)
|
85
|
+
current = current_path ? vertex_by_name_path(current_path) : self
|
86
|
+
raise "#{current_path.inspect} not found" unless current
|
87
|
+
case direction
|
88
|
+
# when /^prev!(?:#{Tengine::Core::Validation::BASE_NAME.format})/
|
89
|
+
when /^(prev|next)!(#{NAME_PATH_PART})$/ then
|
90
|
+
job = $2 ? current.vertex_by_name_path($2) : self
|
91
|
+
job.send("#{$1}_edges").first
|
92
|
+
when /^(start|end|finally)!(#{NAME_PATH_PART})$/ then
|
93
|
+
job = $2 ? current.vertex_by_name_path($2) : self
|
94
|
+
job.child_by_name($1)
|
95
|
+
when /^(start|end|finally)$/ then
|
96
|
+
current.child_by_name($1)
|
97
|
+
when /^(#{NAME_PART})~(#{NAME_PART})$/ then
|
98
|
+
job1 = current.child_by_name($1)
|
99
|
+
job2 = current.child_by_name($2)
|
100
|
+
job1.next_edges.detect{|edge| edge.destination_id == job2.id}
|
101
|
+
when /^(fork|join)!(#{NAME_PART})~(#{NAME_PART})$/ then
|
102
|
+
klass = Tengine::Job.const_get($1.capitalize)
|
103
|
+
job1 = current.child_by_name($2)
|
104
|
+
job2 = current.child_by_name($3)
|
105
|
+
paths = PathFinder.new(job1, job2).process
|
106
|
+
paths.each do |path|
|
107
|
+
path.each do |element|
|
108
|
+
return element if element.is_a?(klass)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
when /^fork~join!(#{NAME_PART})~(#{NAME_PART})$/ then
|
112
|
+
job1 = current.child_by_name($1)
|
113
|
+
job2 = current.child_by_name($2)
|
114
|
+
paths = PathFinder.new(job1, job2).process
|
115
|
+
paths.each do |path|
|
116
|
+
path.each do |element|
|
117
|
+
if element.is_a?(Tengine::Job::Edge)
|
118
|
+
if element.origin.is_a?(Tengine::Job::Fork) &&
|
119
|
+
element.destination.is_a?(Tengine::Job::Join)
|
120
|
+
return element
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
else
|
126
|
+
current.child_by_name(direction)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# element!メソッドは指定されたnotationによって対象となる要素が見つからなかった場合はnilを返します。
|
131
|
+
# 例外をraiseさせたい場合は element!メソッドを使ってください。
|
132
|
+
def element!(notation, *args)
|
133
|
+
result = element(notation, *args)
|
134
|
+
raise NotFound.new(self, notation) unless result
|
135
|
+
result
|
136
|
+
end
|
137
|
+
|
138
|
+
class PathFinder
|
139
|
+
def initialize(origin, dest)
|
140
|
+
@origin, @dest = origin, dest
|
141
|
+
end
|
142
|
+
|
143
|
+
def process
|
144
|
+
@routes = []
|
145
|
+
@current_route = []
|
146
|
+
@origin.accept_visitor(self)
|
147
|
+
@routes.select{|route| route.last == @dest}
|
148
|
+
end
|
149
|
+
|
150
|
+
def visit(element)
|
151
|
+
@current_route << element
|
152
|
+
if element.is_a?(Tengine::Job::Edge)
|
153
|
+
element.destination.accept_visitor(self)
|
154
|
+
else
|
155
|
+
@routes << @current_route.dup
|
156
|
+
return if element == @dest
|
157
|
+
element.next_edges.each do |edge|
|
158
|
+
bak = @current_route.dup
|
159
|
+
begin
|
160
|
+
edge.accept_visitor(self)
|
161
|
+
ensure
|
162
|
+
@current_route = bak
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|