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,55 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# テンプレートから生成された実行時に使用されるジョブネットを表すVertex。
|
5
|
+
class Tengine::Job::JobnetActual < Tengine::Job::Jobnet
|
6
|
+
include Tengine::Job::ScriptExecutable
|
7
|
+
include Tengine::Job::Executable
|
8
|
+
include Tengine::Job::Stoppable
|
9
|
+
include Tengine::Job::Jobnet::JobStateTransition
|
10
|
+
include Tengine::Job::Jobnet::JobnetStateTransition
|
11
|
+
|
12
|
+
field :was_expansion, :type => Boolean # テンプレートがTenigne::Job::Expansionであった場合にtrueです。
|
13
|
+
|
14
|
+
# was_expansionがtrueなら元々のtemplateへの参照が必要なのでRootJobnetActualだけでなく
|
15
|
+
# JobnetActualでもtemplateが必要です。
|
16
|
+
belongs_to :template, :inverse_of => :root_jobnet_actuals, :index => true, :class_name => "Tengine::Job::RootJobnetTemplate"
|
17
|
+
|
18
|
+
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#D26C1
|
19
|
+
STATE_TRANSITION_METHODS = [:transmit, :activate, :ack, :finish, :succeed, :fail, :fire_stop, :stop, :reset].freeze
|
20
|
+
STATE_TRANSITION_METHODS.each do |method_name|
|
21
|
+
class_eval(<<-END_OF_METHOD, __FILE__, __LINE__ + 1)
|
22
|
+
def #{method_name}(signal, &block)
|
23
|
+
script_executable? ?
|
24
|
+
job_#{method_name}(signal, &block) :
|
25
|
+
jobnet_#{method_name}(signal, &block)
|
26
|
+
end
|
27
|
+
END_OF_METHOD
|
28
|
+
end
|
29
|
+
|
30
|
+
def root_or_expansion
|
31
|
+
p = parent
|
32
|
+
p.nil? ? self : p.was_expansion ? p : p.root_or_expansion
|
33
|
+
end
|
34
|
+
|
35
|
+
# https://www.pivotaltracker.com/story/show/23329935
|
36
|
+
|
37
|
+
def stop_reason= r
|
38
|
+
super
|
39
|
+
children.each do |i|
|
40
|
+
if i.respond_to?(:chained_box?) && i.chained_box?
|
41
|
+
i.stop_reason = r
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def stopped_at= t
|
47
|
+
super
|
48
|
+
children.each do |i|
|
49
|
+
if i.respond_to?(:chained_box?) && i.chained_box?
|
50
|
+
i.stopped_at = t
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# ForkやJoinの継承元となるVertex。特に状態は持たない。
|
5
|
+
class Tengine::Job::Junction < Tengine::Job::Vertex
|
6
|
+
|
7
|
+
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#D26C1
|
8
|
+
def transmit(signal)
|
9
|
+
complete_origin_edge(signal, :except_closed => true)
|
10
|
+
# transmitted?で判断すると、closedなものに対する処理を考慮できないので、alive?を使って判断します
|
11
|
+
# activate(signal) if prev_edges.all?(&:transmitted?)
|
12
|
+
execution = signal.execution
|
13
|
+
predicate = execution.retry ? :alive_or_closing_or_closed? : :alive_or_closing?
|
14
|
+
activate(signal) unless prev_edges.any?(&predicate)
|
15
|
+
end
|
16
|
+
|
17
|
+
def activatable?
|
18
|
+
prev_edges.all?(&:transmitted?)
|
19
|
+
end
|
20
|
+
|
21
|
+
def activate(signal)
|
22
|
+
signal.leave(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset(signal)
|
26
|
+
signal.leave(self, :reset)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# 終了対象となりうるVertexで使用するモジュール
|
5
|
+
module Tengine::Job::Killing
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
require 'tengine/core'
|
10
|
+
include Tengine::Core::CollectionAccessible
|
11
|
+
|
12
|
+
field :killing_signals, :type => Array # 強制停止時にプロセスに送るシグナルの配列
|
13
|
+
array_text_accessor :killing_signals
|
14
|
+
|
15
|
+
field :killing_signal_interval, :type => Integer # 強制停止時にkilling_signalsで定義されるシグナルを順次送信する間隔。
|
16
|
+
end
|
17
|
+
|
18
|
+
DEFAULT_KILLING_SIGNAL_INTERVAL = 5
|
19
|
+
|
20
|
+
def actual_killing_signals
|
21
|
+
killing_signals ? killing_signals :
|
22
|
+
(parent ? parent.actual_killing_signals : ['KILL'])
|
23
|
+
end
|
24
|
+
|
25
|
+
def actual_killing_signal_interval
|
26
|
+
killing_signals ? killing_signal_interval :
|
27
|
+
(parent ? parent.actual_killing_signal_interval : DEFAULT_KILLING_SIGNAL_INTERVAL)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Tengine::Job::MmCompatibility::Connectable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
alias_method :vm_instance_name , :server_name
|
6
|
+
alias_method :vm_instance_name=, :server_name=
|
7
|
+
alias_method :instance_name , :server_name
|
8
|
+
alias_method :instance_name=, :server_name=
|
9
|
+
alias_method :user_group_credential_name , :credential_name
|
10
|
+
alias_method :user_group_credential_name=, :credential_name=
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'tengine/job'
|
2
|
+
|
3
|
+
module Tengine::Job::NamePath
|
4
|
+
|
5
|
+
SEPARATOR = '/'.freeze
|
6
|
+
ABSOLUTE_PATH_REGEXP = /^\//.freeze
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def absolute?(name_path)
|
10
|
+
ABSOLUTE_PATH_REGEXP =~ name_path
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def name_path
|
15
|
+
name = respond_to?(:name) ? self.name : self.class.name.split('::').last.underscore
|
16
|
+
parent ? "#{parent.name_path}#{SEPARATOR}#{name}" :
|
17
|
+
"#{SEPARATOR}#{name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def name_path_until_expansion
|
21
|
+
name = respond_to?(:name) ? self.name : self.class.name.split('::').last.underscore
|
22
|
+
if self.respond_to?(:was_expansion) && self.was_expansion
|
23
|
+
"#{SEPARATOR}#{name}"
|
24
|
+
else
|
25
|
+
parent ? "#{parent.name_path_until_expansion}#{SEPARATOR}#{name}" :
|
26
|
+
"#{SEPARATOR}#{name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# ルートジョブネットとして必要な情報に関するモジュール
|
5
|
+
module Tengine::Job::Root
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include Tengine::Core::OptimisticLock
|
10
|
+
set_locking_field :version
|
11
|
+
|
12
|
+
belongs_to :category, :inverse_of => :root_jobnet_templates, :index => true, :class_name => "Tengine::Job::Category"
|
13
|
+
|
14
|
+
field :version, :type => Integer, :default => 0 # ジョブネット全体を更新する際の楽観的ロックのためのバージョン。更新するたびにインクリメントされます。
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# 実行時のルートジョブネットを表すVertex
|
5
|
+
class Tengine::Job::RootJobnetActual < Tengine::Job::JobnetActual
|
6
|
+
include Tengine::Job::Root
|
7
|
+
|
8
|
+
has_many :executions, :inverse_of => :root_jobnet, :class_name => "Tengine::Job::Execution"
|
9
|
+
|
10
|
+
|
11
|
+
def rerun(*args)
|
12
|
+
options = args.extract_options!
|
13
|
+
sender = options.delete(:sender) || Tengine::Event.default_sender
|
14
|
+
options = options.merge({
|
15
|
+
:retry => true,
|
16
|
+
:root_jobnet_id => self.id,
|
17
|
+
})
|
18
|
+
result = Tengine::Job::Execution.new(options)
|
19
|
+
result.target_actual_ids ||= []
|
20
|
+
result.target_actual_ids += args.flatten
|
21
|
+
result.save!
|
22
|
+
sender.wait_for_connection do
|
23
|
+
sender.fire(:'start.execution.job.tengine', :properties => {
|
24
|
+
:execution_id => result.id.to_s
|
25
|
+
})
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_with_lock(*args)
|
31
|
+
super(*args) do
|
32
|
+
Tengine::Job.test_harness_hook("before yield in update_with_lock")
|
33
|
+
yield if block_given?
|
34
|
+
Tengine::Job.test_harness_hook("after yield in update_with_lock")
|
35
|
+
end
|
36
|
+
Tengine::Job.test_harness_hook("after update_with_lock")
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
|
4
|
+
# DSLを評価して登録されるルートジョブネットを表すVertex
|
5
|
+
class Tengine::Job::RootJobnetTemplate < Tengine::Job::JobnetTemplate
|
6
|
+
include Tengine::Job::Root
|
7
|
+
include Tengine::Core::FindByName
|
8
|
+
|
9
|
+
field :dsl_filepath, :type => String # ルートジョブネットを定義した際にロードされたDSLのファイル名(Tengine::Core::Config#dsl_dir_pathからの相対パス)
|
10
|
+
field :dsl_lineno , :type => Integer # ルートジョブネットを定義するjobnetメソッドの呼び出しの、ロードされたDSLのファイルでの行番号
|
11
|
+
field :dsl_version , :type => String # ルートジョブネットを定義した際のDSLのバージョン
|
12
|
+
|
13
|
+
def actual_class
|
14
|
+
Tengine::Job::RootJobnetActual
|
15
|
+
end
|
16
|
+
def generate(klass = actual_class)
|
17
|
+
result = super(klass)
|
18
|
+
result.template = self
|
19
|
+
result
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(options = {})
|
23
|
+
event_sender = options.delete(:sender) || Tengine::Event.default_sender
|
24
|
+
actual = generate
|
25
|
+
actual.save!
|
26
|
+
result = Tengine::Job::Execution.create!(
|
27
|
+
(options || {}).update(:root_jobnet_id => actual.id)
|
28
|
+
)
|
29
|
+
event_sender.fire(:"start.execution.job.tengine", :properties => {
|
30
|
+
:execution_id => result.id,
|
31
|
+
:root_jobnet_id => actual.id,
|
32
|
+
:target_jobnet_id => actual.id
|
33
|
+
})
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_duplication
|
38
|
+
return nil unless self.new_record?
|
39
|
+
self.class.find_by_name(name, :version => self.dsl_version)
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
# Tengine::Core::FindByName で定義しているクラスメソッドfind_by_nameを上書きしています
|
44
|
+
def find_by_name(name, options = {})
|
45
|
+
version = options[:version] || Tengine::Core::Setting.dsl_version
|
46
|
+
first(:conditions => {:name => name, :dsl_version => version})
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job'
|
3
|
+
require 'tengine/resource/net_ssh'
|
4
|
+
|
5
|
+
# ジョブとして実際にスクリプトを実行する処理をまとめるモジュール。
|
6
|
+
# Tengine::Job::JobnetActualと、Tengine::Job::ScriptActualがincludeします
|
7
|
+
module Tengine::Job::ScriptExecutable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
class Error < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
included do
|
14
|
+
include Tengine::Core::CollectionAccessible
|
15
|
+
|
16
|
+
field :executing_pid, :type => String # 実行しているプロセスのPID
|
17
|
+
field :exit_status , :type => String # 終了したプロセスが返した終了ステータス
|
18
|
+
field :error_messages, :type => Array # エラーになった場合のメッセージを保持する配列。再実行時に追加される場合は末尾に追加されます。
|
19
|
+
array_text_accessor :error_messages, :delimeter => "\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(execution)
|
23
|
+
return ack(@acked_pid) if @acked_pid
|
24
|
+
cmd = build_command(execution)
|
25
|
+
# puts "cmd:\n" << cmd
|
26
|
+
execute(cmd) do |ch, data|
|
27
|
+
if signal = execution.signal
|
28
|
+
# signal.data = {:executing_pid => data.strip}
|
29
|
+
# ack(signal)
|
30
|
+
pid = data.strip
|
31
|
+
signal.callback = lambda do
|
32
|
+
signal.data = {:executing_pid => pid}
|
33
|
+
|
34
|
+
# このブロック内の処理はupdate_with_lockによって複数回実行されることがあります。
|
35
|
+
# 1回目と同じリロードされていないオブジェクトを2回目以降に使用すると、1回目の変更が残っているので
|
36
|
+
# そのオブジェクトに対して処理を行うのはNGです。
|
37
|
+
# self.ack(signal) # これはNG
|
38
|
+
|
39
|
+
# このブロックが実行されるたびに、rootからselfと同じidのオブジェクトを新たに取得する必要があります。
|
40
|
+
job = root.vertex(self.id)
|
41
|
+
job.ack(signal)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(cmd)
|
48
|
+
raise "actual_server not found for #{self.name_path.inspect}" unless actual_server
|
49
|
+
Tengine.logger.info("connecting to #{actual_server.hostname_or_ipv4}")
|
50
|
+
port = actual_server.properties["ssh_port"] || 22
|
51
|
+
keys_only = actual_credential.auth_type_cd == :ssh_public_key
|
52
|
+
Net::SSH.start(actual_server.hostname_or_ipv4, actual_credential, :port => port, :logger => Tengine.logger, :keys_only => keys_only) do |ssh|
|
53
|
+
# see http://net-ssh.github.com/ssh/v2/api/classes/Net/SSH/Connection/Channel.html
|
54
|
+
ssh.open_channel do |channel|
|
55
|
+
Tengine.logger.info("now exec on ssh: " << cmd)
|
56
|
+
channel.exec(cmd.force_encoding("binary")) do |ch, success|
|
57
|
+
raise Error, "could not execute command" unless success
|
58
|
+
|
59
|
+
channel.on_close do |ch|
|
60
|
+
# puts "channel is closing!"
|
61
|
+
end
|
62
|
+
|
63
|
+
channel.on_data do |ch, data|
|
64
|
+
Tengine.logger.debug("got stdout: #{data}")
|
65
|
+
yield(ch, data) if block_given?
|
66
|
+
end
|
67
|
+
|
68
|
+
channel.on_extended_data do |ch, type, data|
|
69
|
+
self.error_messages ||= []
|
70
|
+
self.error_messages += [data]
|
71
|
+
raise Error, "Failure to execute #{self.name_path} via SSH: #{data}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
rescue Tengine::Job::ScriptExecutable::Error
|
78
|
+
raise
|
79
|
+
rescue Mongoid::Errors::DocumentNotFound, SocketError, Net::SSH::AuthenticationFailed => src
|
80
|
+
error = Error.new("[#{src.class.name}] #{src.message}")
|
81
|
+
error.set_backtrace(src.backtrace)
|
82
|
+
raise error
|
83
|
+
rescue Exception
|
84
|
+
# puts "[#{$!.class.name}] #{$!.message}"
|
85
|
+
raise
|
86
|
+
end
|
87
|
+
|
88
|
+
def kill(execution)
|
89
|
+
lines = source_profiles
|
90
|
+
|
91
|
+
if self.executing_pid.blank?
|
92
|
+
Tengine.logger.warn("PID is blank when kill!!\n#{self.inspect}\n " << caller.join("\n "))
|
93
|
+
end
|
94
|
+
|
95
|
+
cmd = executable_command("tengine_job_agent_kill %s %d %s" % [
|
96
|
+
self.executing_pid,
|
97
|
+
self.actual_killing_signal_interval,
|
98
|
+
self.actual_killing_signals.join(","),
|
99
|
+
])
|
100
|
+
lines << cmd
|
101
|
+
cmd = lines.join(' && ')
|
102
|
+
execute(cmd)
|
103
|
+
end
|
104
|
+
|
105
|
+
# def ack(pid)
|
106
|
+
# @acked_pid = pid
|
107
|
+
# self.executing_pid = pid
|
108
|
+
# self.phase_key = :running
|
109
|
+
# self.previous_edges.each{|edge| edge.status_key = :transmitted}
|
110
|
+
# end
|
111
|
+
|
112
|
+
def build_command(execution)
|
113
|
+
result = source_profiles
|
114
|
+
mm_env = build_mm_env(execution).map{|k,v| "#{k}=#{v}"}.join(" ")
|
115
|
+
# Hadoopジョブの場合は環境変数をセットする
|
116
|
+
if is_a?(Tengine::Job::Jobnet) && (jobnet_type_key == :hadoop_job_run)
|
117
|
+
mm_env << ' ' << hadoop_job_env
|
118
|
+
end
|
119
|
+
result << "export #{mm_env}"
|
120
|
+
template_root = root_or_expansion.template
|
121
|
+
if template_root
|
122
|
+
template_job = template_root.vertex_by_name_path(self.name_path_until_expansion)
|
123
|
+
unless template_job
|
124
|
+
raise "job not found #{self.name_path_until_expansion.inspect} in #{template_root.inspect}"
|
125
|
+
end
|
126
|
+
key = Tengine::Job::DslLoader.template_block_store_key(template_job, :preparation)
|
127
|
+
preparation_block = Tengine::Job::DslLoader.template_block_store[key]
|
128
|
+
if preparation_block
|
129
|
+
preparation = instance_eval(&preparation_block)
|
130
|
+
unless preparation.blank?
|
131
|
+
result << preparation
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
unless execution.preparation_command.blank?
|
136
|
+
result << execution.preparation_command
|
137
|
+
end
|
138
|
+
# cmdはユーザーが設定したスクリプトを組み立てたもので、
|
139
|
+
# プロセスの監視/強制停止のためにtengine_job_agent/bin/tengine_job_agent_run
|
140
|
+
# からこれらを実行させるためにはcmdを編集します。
|
141
|
+
# tengine_job_agent_runは、標準出力に監視対象となる起動したプロセスのPIDを出力します。
|
142
|
+
runner_path = ENV["MM_RUNNER_PATH"] || executable_command("tengine_job_agent_run")
|
143
|
+
runner_option = ""
|
144
|
+
# 実装するべきか要検討
|
145
|
+
# runner_option << " --stdout" if execution.keeping_stdout
|
146
|
+
# runner_option << " --stderr" if execution.keeping_stderr
|
147
|
+
# script = "#{runner_path}#{runner_option} -- #{self.script}" # runnerのオプションを指定する際は -- の前に設定してください
|
148
|
+
script = "#{runner_path}#{runner_option} #{self.script}" # runnerのオプションを指定する際は -- の前に設定してください
|
149
|
+
result << script
|
150
|
+
result.join(" && ")
|
151
|
+
end
|
152
|
+
|
153
|
+
def source_profiles
|
154
|
+
# RubyのNet::SSHでは設定ファイルが読み込まれないので、ロードするようにします。
|
155
|
+
# ~/.bash_profile, ~/.bashrc などは非対応。
|
156
|
+
# ファイルが存在していたらsourceで読み込むようにしたいのですが、一旦保留します。
|
157
|
+
# http://www.syns.net/10/
|
158
|
+
["/etc/profile", "/etc/bashrc", "$HOME/.bashrc", "$HOME/.bash_profile"].map do |path|
|
159
|
+
"if [ -f #{path} ]; then source #{path}; fi"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def executable_command(command)
|
164
|
+
if prefix = ENV["MM_CMD_PREFIX"]
|
165
|
+
"#{prefix} #{command}"
|
166
|
+
else
|
167
|
+
command
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# MMから実行されるシェルスクリプトに渡す環境変数のHashを返します。
|
172
|
+
# MM_ACTUAL_JOB_ID : 実行される末端のジョブのMM上でのID
|
173
|
+
# MM_ACTUAL_JOB_ANCESTOR_IDS : 実行される末端のジョブの祖先のMM上でのIDをセミコロンで繋げた文字列 (テンプレートジョブ単位)
|
174
|
+
# MM_FULL_ACTUAL_JOB_ANCESTOR_IDS : 実行される末端のジョブの祖先のMM上でのIDをセミコロンで繋げた文字列 (expansionから展開した単位)
|
175
|
+
# MM_ACTUAL_JOB_NAME_PATH : 実行される末端のジョブのname_path
|
176
|
+
# MM_ACTUAL_JOB_SECURITY_TOKEN : 公開API呼び出しのためのセキュリティ用のワンタイムトークン
|
177
|
+
# MM_TEMPLATE_JOB_ID : テンプレートジョブ(=実行される末端のジョブの元となったジョブ)のID
|
178
|
+
# MM_TEMPLATE_JOB_ANCESTOR_IDS : テンプレートジョブの祖先のMM上でのIDをセミコロンで繋げたもの
|
179
|
+
# MM_SCHEDULE_ID : 実行スケジュールのID
|
180
|
+
# MM_SCHEDULE_ESTIMATED_TIME : 実行スケジュールの見積り時間。単位は分。
|
181
|
+
# MM_SCHEDULE_ESTIMATED_END : 実行スケジュールの見積り終了時刻をYYYYMMDDHHMMSS式で。(できればISO 8601など、タイムゾーンも表現できる標準的な形式の方が良い?)
|
182
|
+
# MM_MASTER_SCHEDULE_ID : マスタースケジュールがあればそのID。マスタースケジュールがない場合は環境変数は指定されません。
|
183
|
+
#
|
184
|
+
# 未実装
|
185
|
+
# MM_FAILED_JOB_ID : ジョブが失敗した場合にrecoverやfinally内のジョブを実行時に設定される、失敗したジョブのMM上でのID。
|
186
|
+
# MM_FAILED_JOB_ANCESTOR_IDS : ジョブが失敗した場合にrecoverやfinally内のジョブを実行時に設定される、失敗したジョブの祖先のMM上でのIDをセミコロンで繋げた文字列。
|
187
|
+
def build_mm_env(execution)
|
188
|
+
result = {
|
189
|
+
"MM_SERVER_NAME" => actual_server_name, # [Tengineの仕様として追加] ジョブの実行サーバ名を設定
|
190
|
+
"MM_ROOT_JOBNET_ID" => root.id.to_s,
|
191
|
+
"MM_TARGET_JOBNET_ID" => parent.id.to_s,
|
192
|
+
"MM_ACTUAL_JOB_ID" => id.to_s,
|
193
|
+
"MM_ACTUAL_JOB_ANCESTOR_IDS" => '"%s"' % ancestors_until_expansion.map(&:id).map(&:to_s).join(';'),
|
194
|
+
"MM_FULL_ACTUAL_JOB_ANCESTOR_IDS" => '"%s"' % ancestors.map(&:id).map(&:to_s).join(';'),
|
195
|
+
"MM_ACTUAL_JOB_NAME_PATH" => name_path.dump,
|
196
|
+
"MM_ACTUAL_JOB_SECURITY_TOKEN" => "", # TODO トークンの生成
|
197
|
+
"MM_SCHEDULE_ID" => execution.id.to_s,
|
198
|
+
"MM_SCHEDULE_ESTIMATED_TIME" => execution.estimated_time,
|
199
|
+
}
|
200
|
+
if estimated_end = execution.actual_estimated_end
|
201
|
+
result["MM_SCHEDULE_ESTIMATED_END"] = estimated_end.strftime("%Y%m%d%H%M%S")
|
202
|
+
end
|
203
|
+
if rjt = root.template
|
204
|
+
t = rjt.find_descendant_by_name_path(self.name_path)
|
205
|
+
unless t
|
206
|
+
template_name_parts = self.name_path_until_expansion.split(Tengine::Job::NamePath::SEPARATOR).select{|s| !s.empty?}
|
207
|
+
root_jobnet_name = template_name_parts.first
|
208
|
+
if rjt = Tengine::Job::RootJobnetTemplate.find_by_name(root_jobnet_name, :version => rjt.dsl_version)
|
209
|
+
t = rjt.find_descendant_by_name_path(self.name_path_until_expansion)
|
210
|
+
raise "template job #{path.inspect} not found in #{rjt.inspect}" unless t
|
211
|
+
else
|
212
|
+
raise "Tengine::Job::RootJobnetTemplate not found #{self.name_path_until_expansion.inspect}"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
result.update({
|
216
|
+
"MM_TEMPLATE_JOB_ID" => t.id.to_s,
|
217
|
+
"MM_TEMPLATE_JOB_ANCESTOR_IDS" => '"%s"' % t.ancestors.map(&:id).map(&:to_s).join(';'),
|
218
|
+
})
|
219
|
+
end
|
220
|
+
# if ms = execution.master_schedule
|
221
|
+
# result.update({
|
222
|
+
# "MM_MASTER_SCHEDULE_ID" => ms.id.to_s,
|
223
|
+
# })
|
224
|
+
# end
|
225
|
+
result
|
226
|
+
end
|
227
|
+
|
228
|
+
def hadoop_job_env
|
229
|
+
s = children.select{|c| c.is_a?(Tengine::Job::Jobnet) && (c.jobnet_type_key == :hadoop_job)}.
|
230
|
+
map{|c| "#{c.name}\\t#{c.id.to_s}\\n"}.join
|
231
|
+
"MM_HADOOP_JOBS=\"#{s}\""
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
end
|