tengine_job 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +23 -0
  4. data/Gemfile.lock +109 -0
  5. data/README.rdoc +20 -0
  6. data/Rakefile +42 -0
  7. data/VERSION +1 -0
  8. data/examples/0004_retry_one_layer.rb +24 -0
  9. data/examples/0004_retry_one_layer.sh +38 -0
  10. data/examples/0005_retry_two_layer.rb +54 -0
  11. data/examples/0005_retry_two_layer.sh +80 -0
  12. data/examples/0006_retry_three_layer.rb +58 -0
  13. data/examples/0006_retry_three_layer.sh +74 -0
  14. data/examples/0007_simple_jobnet.rb +7 -0
  15. data/examples/0021_dynamic_env.rb +20 -0
  16. data/examples/VERSION +1 -0
  17. data/examples/tengine_job_test.sh +10 -0
  18. data/lib/tengine/job.rb +94 -0
  19. data/lib/tengine/job/category.rb +54 -0
  20. data/lib/tengine/job/connectable.rb +43 -0
  21. data/lib/tengine/job/drivers/job_control_driver.rb +82 -0
  22. data/lib/tengine/job/drivers/job_execution_driver.rb +30 -0
  23. data/lib/tengine/job/drivers/jobnet_control_driver.rb +117 -0
  24. data/lib/tengine/job/drivers/schedule_driver.rb +30 -0
  25. data/lib/tengine/job/dsl_binder.rb +12 -0
  26. data/lib/tengine/job/dsl_evaluator.rb +18 -0
  27. data/lib/tengine/job/dsl_loader.rb +180 -0
  28. data/lib/tengine/job/edge.rb +150 -0
  29. data/lib/tengine/job/element_selector_notation.rb +169 -0
  30. data/lib/tengine/job/end.rb +32 -0
  31. data/lib/tengine/job/executable.rb +74 -0
  32. data/lib/tengine/job/execution.rb +141 -0
  33. data/lib/tengine/job/expansion.rb +37 -0
  34. data/lib/tengine/job/fork.rb +6 -0
  35. data/lib/tengine/job/job.rb +23 -0
  36. data/lib/tengine/job/jobnet.rb +173 -0
  37. data/lib/tengine/job/jobnet/builder.rb +150 -0
  38. data/lib/tengine/job/jobnet/job_state_transition.rb +167 -0
  39. data/lib/tengine/job/jobnet/jobnet_state_transition.rb +110 -0
  40. data/lib/tengine/job/jobnet/state_transition.rb +37 -0
  41. data/lib/tengine/job/jobnet_actual.rb +55 -0
  42. data/lib/tengine/job/jobnet_template.rb +10 -0
  43. data/lib/tengine/job/join.rb +6 -0
  44. data/lib/tengine/job/junction.rb +29 -0
  45. data/lib/tengine/job/killing.rb +30 -0
  46. data/lib/tengine/job/mm_compatibility.rb +6 -0
  47. data/lib/tengine/job/mm_compatibility/connectable.rb +13 -0
  48. data/lib/tengine/job/name_path.rb +31 -0
  49. data/lib/tengine/job/root.rb +16 -0
  50. data/lib/tengine/job/root_jobnet_actual.rb +39 -0
  51. data/lib/tengine/job/root_jobnet_template.rb +49 -0
  52. data/lib/tengine/job/script_executable.rb +235 -0
  53. data/lib/tengine/job/signal.rb +121 -0
  54. data/lib/tengine/job/start.rb +20 -0
  55. data/lib/tengine/job/stoppable.rb +15 -0
  56. data/lib/tengine/job/vertex.rb +172 -0
  57. data/lib/tengine_job.rb +3 -0
  58. data/spec/fixtures/rjn_0001_simple_jobnet_builder.rb +42 -0
  59. data/spec/fixtures/rjn_0002_simple_parallel_jobnet_builder.rb +42 -0
  60. data/spec/fixtures/rjn_0003_fork_join_jobnet_builder.rb +61 -0
  61. data/spec/fixtures/rjn_0004_parallel_jobnet_with_finally_fixture.rb +62 -0
  62. data/spec/fixtures/rjn_0005_retry_two_layer_fixture.rb +153 -0
  63. data/spec/fixtures/rjn_0008_expansion_fixture.rb +32 -0
  64. data/spec/fixtures/rjn_0009_tree_sequential_jobnet_builder.rb +174 -0
  65. data/spec/fixtures/rjn_0010_2jobs_and_1job_parallel_jobnet_builder.rb +39 -0
  66. data/spec/fixtures/rjn_0011_nested_fork_jobnet_builder.rb +96 -0
  67. data/spec/fixtures/rjn_0012_nested_and_finally_builder.rb +157 -0
  68. data/spec/fixtures/rjn_1004_hadoop_job_in_jobnet_fixture.rb +105 -0
  69. data/spec/fixtures/rjn_means_root_jobnet +0 -0
  70. data/spec/fixtures/test_credential_fixture.rb +12 -0
  71. data/spec/fixtures/test_server_fixture.rb +28 -0
  72. data/spec/mongoid.yml +35 -0
  73. data/spec/spec_helper.rb +56 -0
  74. data/spec/sshd/.gitignore +1 -0
  75. data/spec/sshd/id_rsa +51 -0
  76. data/spec/sshd/id_rsa.pub +1 -0
  77. data/spec/sshd/ssh_host_rsa_key +51 -0
  78. data/spec/sshd/ssh_host_rsa_key.pub +1 -0
  79. data/spec/sshd/sshd_config +10 -0
  80. data/spec/sshd/sshd_config.erb +11 -0
  81. data/spec/sshd/tengine_job_test.sh +6 -0
  82. data/spec/support/jobnet_fixture_builder.rb +145 -0
  83. data/spec/support/mongo_index_key_log.rb +91 -0
  84. data/spec/tengine/job/category_spec.rb +193 -0
  85. data/spec/tengine/job/connectable_spec.rb +94 -0
  86. data/spec/tengine/job/drivers/job_controll_driver/connection_error_spec.rb +236 -0
  87. data/spec/tengine/job/drivers/job_controll_driver/duplicated_job_start_spec.rb +302 -0
  88. data/spec/tengine/job/drivers/job_controll_driver/expansion_spec.rb +120 -0
  89. data/spec/tengine/job/drivers/job_controll_driver/stop_spec.rb +159 -0
  90. data/spec/tengine/job/drivers/job_controll_driver_spec.rb +623 -0
  91. data/spec/tengine/job/drivers/job_execution_driver_spec.rb +88 -0
  92. data/spec/tengine/job/drivers/jobnet_control_driver/nested_and_finally_spec.rb +472 -0
  93. data/spec/tengine/job/drivers/jobnet_control_driver/nested_jobnet_spec.rb +231 -0
  94. data/spec/tengine/job/drivers/jobnet_control_driver/stop_jobnet_spec.rb +202 -0
  95. data/spec/tengine/job/drivers/jobnet_control_driver_spec.rb +446 -0
  96. data/spec/tengine/job/drivers/schedule_driver_spec.rb +202 -0
  97. data/spec/tengine/job/dsl_binder_spec.rb +36 -0
  98. data/spec/tengine/job/dsl_loader_spec.rb +403 -0
  99. data/spec/tengine/job/dsls/0013_hadoop_job_run.rb +29 -0
  100. data/spec/tengine/job/dsls/0014_join_and_join.rb +19 -0
  101. data/spec/tengine/job/dsls/0015_fork_and_fork.rb +18 -0
  102. data/spec/tengine/job/dsls/0016_complex_fork_and_join.rb +20 -0
  103. data/spec/tengine/job/dsls/0017_finally.rb +15 -0
  104. data/spec/tengine/job/dsls/0018_expansion.rb +23 -0
  105. data/spec/tengine/job/dsls/0019_execute_job_on_event.rb +16 -0
  106. data/spec/tengine/job/dsls/0020_duplicated_jobnet_name.rb +16 -0
  107. data/spec/tengine/job/dsls/1060_test_dir1/1060_test_dir2/0013_hadoop_job_run.rb +29 -0
  108. data/spec/tengine/job/dsls/2003_expansion/expansion_5.rb +11 -0
  109. data/spec/tengine/job/dsls/VERSION +1 -0
  110. data/spec/tengine/job/dynamic_env_spec.rb +95 -0
  111. data/spec/tengine/job/edge_spec.rb +241 -0
  112. data/spec/tengine/job/element_selector_notation_spec.rb +354 -0
  113. data/spec/tengine/job/examples_spec.rb +62 -0
  114. data/spec/tengine/job/execution_spec.rb +100 -0
  115. data/spec/tengine/job/expansion_spec.rb +116 -0
  116. data/spec/tengine/job/hadoop_job_run_spec.rb +65 -0
  117. data/spec/tengine/job/job_spec.rb +4 -0
  118. data/spec/tengine/job/jobnet/1015_complecated_jobnet_spec.rb +72 -0
  119. data/spec/tengine/job/jobnet_actual_spec.rb +175 -0
  120. data/spec/tengine/job/jobnet_spec.rb +399 -0
  121. data/spec/tengine/job/jobnet_template_spec.rb +240 -0
  122. data/spec/tengine/job/killing_spec.rb +91 -0
  123. data/spec/tengine/job/reset_spec.rb +958 -0
  124. data/spec/tengine/job/reset_spec/4056_1_dump.txt +1 -0
  125. data/spec/tengine/job/root_jobnet_actual_spec.rb +89 -0
  126. data/spec/tengine/job/root_jobnet_template_spec.rb +248 -0
  127. data/spec/tengine/job/script_executable_spec.rb +132 -0
  128. data/spec/tengine/job/stoppable_spec.rb +176 -0
  129. data/spec/tengine/job/vertex_spec.rb +25 -0
  130. data/spec/tengine_job_spec.rb +4 -0
  131. data/tengine_job.gemspec +197 -0
  132. data/tmp/log/.gitignore +1 -0
  133. 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,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ # ジョブDSLで定義されるジョブネットを表すVertex。
5
+ class Tengine::Job::JobnetTemplate < Tengine::Job::Jobnet
6
+
7
+ def actual_class
8
+ Tengine::Job::JobnetActual
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ # 複数のVertexの終了を待ちあわせて一つのVertexへSignalを通知する合流のVertex。
5
+ class Tengine::Job::Join < Tengine::Job::Junction
6
+ 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,6 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # MM1との互換性のためのメソッドを提供するモジュール
4
+ module Tengine::Job::MmCompatibility
5
+ autoload :Connectable, "tengine/job/mm_compatibility/connectable"
6
+ 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