xli-dtr 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/CHANGES +21 -0
  2. data/README.rdoc +39 -28
  3. data/Rakefile +1 -1
  4. data/TODO +1 -3
  5. data/bin/dtr +20 -13
  6. data/dtr.gemspec +3 -3
  7. data/lib/dtr/agent/brain.rb +45 -19
  8. data/lib/dtr/agent/herald.rb +8 -10
  9. data/lib/dtr/agent/process_root.rb +23 -0
  10. data/lib/dtr/agent/rails_ext.rb +57 -0
  11. data/lib/dtr/agent/runner.rb +19 -39
  12. data/lib/dtr/agent/sync_codebase.rb +10 -19
  13. data/lib/dtr/agent/sync_logger.rb +1 -0
  14. data/lib/dtr/agent/worker.rb +13 -61
  15. data/lib/dtr/agent/working_env_ext.rb +51 -17
  16. data/lib/dtr/agent/working_status.rb +34 -0
  17. data/lib/dtr/agent.rb +12 -5
  18. data/lib/dtr/facade.rb +19 -17
  19. data/lib/dtr/master.rb +2 -0
  20. data/lib/dtr/monitor.rb +2 -2
  21. data/lib/dtr/raketasks.rb +14 -19
  22. data/lib/dtr/shared/adapter/base.rb +118 -0
  23. data/lib/dtr/shared/adapter/mortality.rb +41 -0
  24. data/lib/dtr/shared/adapter.rb +2 -101
  25. data/lib/dtr/shared/configuration.rb +32 -14
  26. data/lib/dtr/shared/root.rb +20 -0
  27. data/lib/dtr/shared/sync_codebase/master_ext.rb +2 -2
  28. data/lib/dtr/shared/sync_codebase/sync_service.rb +7 -4
  29. data/lib/dtr/shared/utils/cmd.rb +4 -2
  30. data/lib/dtr/shared/utils/env_store.rb +16 -24
  31. data/lib/dtr/shared/working_env.rb +8 -0
  32. data/lib/dtr/shared.rb +1 -0
  33. data/lib/dtr/test_unit/drb_test_runner.rb +1 -1
  34. data/lib/dtr/test_unit/thread_safe_test_result.rb +56 -3
  35. data/lib/dtr.rb +2 -2
  36. data/test/acceptance/dtr_package_task_test.rb +3 -3
  37. data/test/acceptance/general_test.rb +3 -3
  38. data/test/acceptance/rails_ext_test.rb +86 -0
  39. data/test/acceptance/raketasks_test.rb +14 -0
  40. data/test/acceptance/sync_codebase_test.rb +8 -31
  41. data/test/acceptance/sync_logger_test.rb +6 -3
  42. data/test/agent_helper.rb +24 -16
  43. data/test/logger_stub.rb +3 -3
  44. data/test/test_helper.rb +12 -9
  45. data/test/unit/adapter_test.rb +1 -1
  46. data/test/unit/configuration_test.rb +36 -4
  47. data/test/unit/database_initializer_test.rb +36 -0
  48. data/test/unit/facade_test.rb +11 -1
  49. data/test/unit/thread_safe_test_result_test.rb +41 -0
  50. data/test/unit/working_env_test.rb +11 -21
  51. data/testdata/rails_projects/simple_project/README +256 -0
  52. data/testdata/rails_projects/simple_project/Rakefile +10 -0
  53. data/testdata/rails_projects/simple_project/app/controllers/application.rb +15 -0
  54. data/testdata/rails_projects/simple_project/app/controllers/products_controller.rb +5 -0
  55. data/testdata/rails_projects/simple_project/app/helpers/application_helper.rb +3 -0
  56. data/testdata/rails_projects/simple_project/app/helpers/products_helper.rb +2 -0
  57. data/testdata/rails_projects/simple_project/app/models/product.rb +5 -0
  58. data/testdata/rails_projects/simple_project/app/views/products/index.rhtml +7 -0
  59. data/testdata/rails_projects/simple_project/config/boot.rb +109 -0
  60. data/testdata/rails_projects/simple_project/config/database.yml +19 -0
  61. data/testdata/rails_projects/simple_project/config/database.yml.mysql +12 -0
  62. data/testdata/rails_projects/simple_project/config/environment.rb +67 -0
  63. data/testdata/rails_projects/simple_project/config/environments/development.rb +17 -0
  64. data/testdata/rails_projects/simple_project/config/environments/production.rb +22 -0
  65. data/testdata/rails_projects/simple_project/config/environments/test.rb +22 -0
  66. data/testdata/rails_projects/simple_project/config/initializers/inflections.rb +10 -0
  67. data/testdata/rails_projects/simple_project/config/initializers/mime_types.rb +5 -0
  68. data/testdata/rails_projects/simple_project/config/initializers/new_rails_defaults.rb +17 -0
  69. data/testdata/rails_projects/simple_project/config/routes.rb +43 -0
  70. data/testdata/rails_projects/simple_project/db/migrate/20081027133744_create_products.rb +13 -0
  71. data/testdata/rails_projects/simple_project/db/schema.rb +21 -0
  72. data/testdata/rails_projects/simple_project/doc/README_FOR_APP +2 -0
  73. data/testdata/rails_projects/simple_project/public/404.html +30 -0
  74. data/testdata/rails_projects/simple_project/public/422.html +30 -0
  75. data/testdata/rails_projects/simple_project/public/500.html +30 -0
  76. data/testdata/rails_projects/simple_project/public/dispatch.cgi +10 -0
  77. data/testdata/rails_projects/simple_project/public/dispatch.fcgi +24 -0
  78. data/testdata/rails_projects/simple_project/public/dispatch.rb +10 -0
  79. data/testdata/rails_projects/simple_project/public/favicon.ico +0 -0
  80. data/testdata/rails_projects/simple_project/public/images/rails.png +0 -0
  81. data/testdata/rails_projects/simple_project/public/index.html +274 -0
  82. data/testdata/rails_projects/simple_project/public/javascripts/application.js +2 -0
  83. data/testdata/rails_projects/simple_project/public/javascripts/controls.js +963 -0
  84. data/testdata/rails_projects/simple_project/public/javascripts/dragdrop.js +972 -0
  85. data/testdata/rails_projects/simple_project/public/javascripts/effects.js +1120 -0
  86. data/testdata/rails_projects/simple_project/public/javascripts/prototype.js +4225 -0
  87. data/testdata/rails_projects/simple_project/public/robots.txt +5 -0
  88. data/testdata/rails_projects/simple_project/script/about +4 -0
  89. data/testdata/rails_projects/simple_project/script/console +3 -0
  90. data/testdata/rails_projects/simple_project/script/dbconsole +3 -0
  91. data/testdata/rails_projects/simple_project/script/destroy +3 -0
  92. data/testdata/rails_projects/simple_project/script/generate +3 -0
  93. data/testdata/rails_projects/simple_project/script/performance/benchmarker +3 -0
  94. data/testdata/rails_projects/simple_project/script/performance/profiler +3 -0
  95. data/testdata/rails_projects/simple_project/script/performance/request +3 -0
  96. data/testdata/rails_projects/simple_project/script/plugin +3 -0
  97. data/testdata/rails_projects/simple_project/script/process/inspector +3 -0
  98. data/testdata/rails_projects/simple_project/script/process/reaper +3 -0
  99. data/testdata/rails_projects/simple_project/script/process/spawner +3 -0
  100. data/testdata/rails_projects/simple_project/script/runner +3 -0
  101. data/testdata/rails_projects/simple_project/script/server +3 -0
  102. data/testdata/rails_projects/simple_project/test/fixtures/products.yml +9 -0
  103. data/testdata/rails_projects/simple_project/test/functional/products_controller_test.rb +11 -0
  104. data/testdata/rails_projects/simple_project/test/test_helper.rb +38 -0
  105. data/testdata/rails_projects/simple_project/test/unit/product_test.rb +27 -0
  106. data/testdata/raketasks/Rakefile +1 -1
  107. metadata +90 -4
  108. data/testdata/verify_dir_pwd/Rakefile +0 -6
  109. data/testdata/verify_dir_pwd/verify_dir_pwd_test_case.rb +0 -10
@@ -17,27 +17,18 @@ module DTR
17
17
  module WorkingEnvExt
18
18
  include SyncService
19
19
 
20
- def self.included(base)
21
- base.alias_method_chain :setup_env, :sync_codebase
22
- base.alias_method_chain :working_dir, :sync_codebase
23
- end
24
-
25
- def setup_env_with_sync_codebase(setup_env_cmd)
26
- unless same_working_dir_with_master_process?
27
- Dir.chdir(working_dir_without_sync_codebase) do
28
- sync_codebase
20
+ def synchronize_for(runners)
21
+ Dir.chdir(base_dir) do
22
+ sync_codebase do
23
+ FileUtils.rm_rf(package_name)
24
+ do_work(unpackage_cmd)
25
+ runners.each do |runner_name|
26
+ dir = File.expand_path escape_dir(runner_name)
27
+ FileUtils.rm_rf(dir)
28
+ FileUtils.cp_r(package_name, dir)
29
+ end
29
30
  end
30
31
  end
31
- setup_env_without_sync_codebase(setup_env_cmd)
32
- end
33
-
34
- def working_dir_with_sync_codebase
35
- same_working_dir_with_master_process? ? Dir.pwd : File.join(working_dir_without_sync_codebase, package_name)
36
- end
37
-
38
- private
39
- def same_working_dir_with_master_process?
40
- self[:host] == Socket.gethostname && self[:pwd] == Dir.pwd
41
32
  end
42
33
  end
43
34
  end
@@ -61,6 +61,7 @@ module DTR
61
61
  private
62
62
  def with_decorating_message(level, msg, &block)
63
63
  raise 'Should not use block to send log remotely' if block_given?
64
+ msg = "#{ENV['DTR_RUNNER_NAME']}: #{msg}"if ENV['DTR_RUNNER_NAME']
64
65
  @logger.send(level, decorate_message(msg))
65
66
  end
66
67
  end
@@ -15,74 +15,26 @@
15
15
  module DTR
16
16
 
17
17
  module Agent
18
- # Worker works during one dtr test task running.
19
- # Worker manages Herald & Runner processes life cycle.
18
+ # Worker watchs runner processes.
20
19
  class Worker
21
- def initialize(runner_names, agent_env_setup_cmd)
22
- @runner_names = runner_names.is_a?(Array) ? runner_names : [runner_names.to_s]
23
- @agent_env_setup_cmd = agent_env_setup_cmd
24
- @runner_pids = []
25
- @herald = nil
26
- @working_env_key = :working_env
27
- @env_store = EnvStore.new
20
+ def initialize
21
+ @runners_group = ThreadGroup.new
28
22
  end
29
23
 
30
- def launch
31
- DTR.info {"=> Agent worker started at: #{Dir.pwd}, pid: #{Process.pid}"}
32
- setup
33
- begin
34
- run
35
- ensure
36
- teardown
37
- DTR.info {"Agent worker is dieing"}
24
+ def watch_runners
25
+ DTR.configuration.agent_runners.each do |name|
26
+ runner_thread = Thread.start { DTR.run_script("DTR::Agent::Runner.new(#{name.inspect}).start") }
27
+ runner_thread[:runner_name] = name
28
+ @runners_group.add runner_thread
38
29
  end
39
- end
40
-
41
- private
42
- def setup
43
- @env_store[@working_env_key] = nil
44
- end
45
-
46
- def teardown
47
- unless @runner_pids.blank?
48
- @runner_pids.each{ |pid| DTR.kill_process pid }
49
- DTR.info {"=> All runners(#{@runner_pids.join(", ")}) were killed." }
50
- @runner_pids = []
51
- end
52
- if @herald
53
- DTR.kill_process @herald
54
- @herald = nil
55
- DTR.info {"=> Herald is killed."}
56
- end
57
- end
58
-
59
- def run
60
- herald
61
- runners
62
- DTR.info {"=> All agent worker sub processes exited."}
63
- end
64
-
65
- def herald
66
- @herald = DTR.fork_process { Herald.new @working_env_key, @agent_env_setup_cmd, @runner_names }
67
- Process.waitpid @herald
68
- exit(-1) unless $?.exitstatus == 0
69
- end
70
30
 
71
- def runners
72
- working_env = @env_store[@working_env_key]
31
+ yield
73
32
 
74
- @runner_names.each do |name|
75
- @runner_pids << DTR.fork_process {
76
- at_exit {
77
- # exit anyway, for DRb may hang on the process to be a deadwalk
78
- exit!
79
- }
80
- working_env.within do
81
- Runner.start name, working_env
82
- end
83
- }
33
+ while @runners_group.list.length > 0
34
+ alive_runners = @runners_group.list.collect {|r| r[:runner_name]}
35
+ DTR.info { "Waiting for #{alive_runners.join(', ').downcase} shutdown" }
36
+ sleep 1
84
37
  end
85
- Process.waitall
86
38
  end
87
39
  end
88
40
  end
@@ -14,33 +14,67 @@
14
14
 
15
15
  module DTR
16
16
  module Agent
17
- # Provides working environment setup beheaviours for Herald & Runner process
18
17
  module WorkingEnvExt
19
- def within
20
- ENV['DTR_MASTER_ENV'] = self[:dtr_master_env]
18
+ def base_dir
19
+ return @base_dir if defined?(@base_dir)
20
+ project_specific_len = 20
21
+ project_name = self[:pwd].length > project_specific_len ? self[:pwd][-project_specific_len..-1] : self[:pwd]
22
+ @base_dir = File.expand_path FileUtils.mkdir_p(File.join(escape_dir(self[:host]), escape_dir(project_name)))
23
+ end
24
+
25
+ def load_environment(&block)
26
+ working_dir = FileUtils.mkdir_p(File.join(base_dir, escape_dir(ENV['DTR_RUNNER_NAME'])))
21
27
  Dir.chdir(working_dir) do
22
- yield
28
+ DTR.info "Loading environment at #{Dir.pwd}"
29
+
30
+ ENV['DTR_MASTER_ENV'] = dtr_master_env
31
+
32
+ setup_environment
33
+
34
+ append_libs_to_load_path
35
+ require_files
36
+
37
+ block.call
23
38
  end
24
39
  end
25
40
 
26
- def setup_env(setup_env_cmd)
27
- within do
28
- Cmd.execute(setup_env_cmd || self[:agent_env_setup_cmd])
41
+ private
42
+
43
+ def setup_environment_command
44
+ DTR.configuration.agent_env_setup_cmd || self.agent_env_setup_cmd
45
+ end
46
+
47
+ def setup_environment
48
+ unless Cmd.execute(setup_environment_command)
49
+ raise "Stopped for setup working environment failed."
29
50
  end
30
51
  end
31
52
 
32
- private
33
- def escape(str)
34
- str.gsub(/[^a-zA-Z0-9]/, '_')
53
+ def escape_dir(str)
54
+ str.to_s.gsub(/[^a-zA-Z0-9]/, '_')
35
55
  end
36
56
 
37
- def working_dir
38
- return @working_dir if defined?(@working_dir)
39
- project_specific_len = 20
40
- project_name = self[:pwd].length > project_specific_len ? self[:pwd][-project_specific_len..-1] : self[:pwd]
41
- @working_dir = File.join(escape(self[:host]), escape(project_name))
42
- FileUtils.mkdir_p(@working_dir)
43
- @working_dir
57
+ def append_libs_to_load_path
58
+ libs.select{ |lib| !$LOAD_PATH.include?(lib) && File.exists?(lib) }.each do |lib|
59
+ $LOAD_PATH << lib
60
+ DTR.debug {"appended lib: #{lib}"}
61
+ end
62
+ DTR.info {"libs appended"}
63
+ DTR.debug {"$LOAD_PATH: #{$LOAD_PATH.inspect}"}
64
+ end
65
+
66
+ def require_files
67
+ files.each do |f|
68
+ begin
69
+ #use require instead of load to avoid load file twice.
70
+ require f unless f =~ /^-/
71
+ DTR.debug {"required #{f}"}
72
+ rescue LoadError => e
73
+ DTR.error {"No such file to load -- #{f}"}
74
+ DTR.debug {"Environment: #{self}"}
75
+ end
76
+ end
77
+ DTR.info {"test files loaded"}
44
78
  end
45
79
  end
46
80
  end
@@ -0,0 +1,34 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module DTR
16
+ module Agent
17
+ module WorkingStatus
18
+ WORKING_STATUS_KEY = :runners_working_status
19
+ def runners_should_be_working
20
+ @store[WORKING_STATUS_KEY] = Time.now
21
+ end
22
+
23
+ def runners_should_be_working?
24
+ if time = @store[WORKING_STATUS_KEY]
25
+ (time - Time.now) <= follower_listen_heartbeat_timeout
26
+ end
27
+ end
28
+
29
+ def agent_is_going_to_sleep
30
+ @store[WORKING_STATUS_KEY] = nil
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/dtr/agent.rb CHANGED
@@ -15,8 +15,11 @@
15
15
  require 'dtr/shared'
16
16
  require 'dtr/agent/working_env_ext'
17
17
  require 'dtr/agent/sync_codebase'
18
+ require 'dtr/agent/rails_ext'
19
+ require 'dtr/agent/working_status'
18
20
 
19
21
  require 'dtr/agent/sync_logger'
22
+ require 'dtr/agent/process_root'
20
23
  require 'dtr/agent/brain'
21
24
  require 'dtr/agent/worker'
22
25
  require 'dtr/agent/test_unit'
@@ -24,15 +27,19 @@ require 'dtr/agent/herald'
24
27
  require 'dtr/agent/test_case'
25
28
  require 'dtr/agent/runner'
26
29
 
30
+ DTR.logger('dtr_agent.log')
31
+
27
32
  module DTR
28
33
  module Agent
29
- def start(runner_names=["Distributed Test Runner"], agent_env_setup_cmd=nil)
30
- DTR.logger('dtr_agent.log')
31
- Brain.new(runner_names, agent_env_setup_cmd).hypnotize
34
+ def start(action=:hypnotize)
35
+ Brain.new.send(action)
32
36
  end
33
37
 
34
38
  module_function :start
39
+
40
+ WorkingEnv.send(:include, WorkingEnvExt)
41
+ WorkingEnv.send(:include, SyncCodebase::WorkingEnvExt)
42
+ WorkingEnv.send(:include, RailsExt::WorkingEnvExt)
43
+ Configuration.send(:include, WorkingStatus)
35
44
  end
36
- WorkingEnv.send(:include, Agent::WorkingEnvExt)
37
- WorkingEnv.send(:include, SyncCodebase::WorkingEnvExt)
38
45
  end
data/lib/dtr/facade.rb CHANGED
@@ -15,13 +15,8 @@
15
15
  module DTR
16
16
  module Facade
17
17
  def start_agent
18
- launch_agent(DTR_AGENT_OPTIONS[:runners], DTR_AGENT_OPTIONS[:agent_env_setup_cmd])
19
- end
20
-
21
- def launch_agent(names, setup=nil)
22
18
  require 'dtr/agent'
23
- names = names || "DTR(#{Time.now})"
24
- DTR::Agent.start(names, setup)
19
+ DTR::Agent.start
25
20
  end
26
21
 
27
22
  def lib_path
@@ -29,37 +24,44 @@ module DTR
29
24
  end
30
25
 
31
26
  def broadcast_list=(list)
32
- require 'dtr/shared'
33
27
  DTR.configuration.broadcast_list = list
34
28
  DTR.configuration.save
35
29
  end
36
30
 
37
31
  def agent_listen_port=(port)
38
- require 'dtr/shared'
39
32
  DTR.configuration.agent_listen_port = port
40
33
  DTR.configuration.save
41
34
  end
42
35
 
43
36
  def group=(group)
44
- require 'dtr/shared'
45
37
  DTR.configuration.group = group
46
38
  DTR.configuration.save
47
39
  end
48
40
 
41
+ def agent_env_setup_cmd=(cmd)
42
+ DTR.configuration.agent_env_setup_cmd = cmd
43
+ DTR.configuration.save
44
+ end
45
+
46
+ def agent_runners=(runners)
47
+ DTR.configuration.agent_runners = runners
48
+ DTR.configuration.save
49
+ end
50
+
51
+ def agent_runners
52
+ DTR.configuration.agent_runners
53
+ end
54
+
49
55
  def monitor
50
56
  require 'dtr/monitor'
51
57
  DTR.logger('dtr_monitor.log')
52
58
  Monitor.new.start
53
59
  end
54
60
 
55
- # For safe fork & kill sub process, should use Process.kill and Process.fork
56
- # At least have problem on ruby 1.8.6 114 with Kernel#kill & fork
57
- def kill_process(pid)
58
- Process.kill 'TERM', pid rescue nil
59
- end
60
-
61
- def fork_process(&block)
62
- Process.fork(&block)
61
+ DTR_CMD = File.expand_path(File.dirname(__FILE__) + '/../../bin/dtr')
62
+ def run_script(cmd)
63
+ dtr = (RUBY_PLATFORM =~ /java/ || ENV['JRUBY_DTR']) ? "jruby -S #{DTR_CMD.inspect}" : "ruby #{DTR_CMD.inspect}"
64
+ system "#{dtr} -e #{cmd.inspect}"
63
65
  end
64
66
  end
65
67
  end
data/lib/dtr/master.rb CHANGED
@@ -24,7 +24,9 @@ module DTR
24
24
  DTR.info {""}
25
25
  DTR.info {"--------------------beautiful line--------------------------"}
26
26
  DTR.info {"Master process started at #{Time.now}"}
27
+ DTR.info {"Master heartbeat interval: #{DTR.configuration.master_heartbeat_interval}"}
27
28
 
29
+ DTR.root = Dir.pwd
28
30
  DTR.configuration.with_rinda_server do
29
31
  provide_working_env WorkingEnv.new
30
32
  with_wakeup_agents(&block)
data/lib/dtr/monitor.rb CHANGED
@@ -23,7 +23,7 @@ module DTR
23
23
  include Adapter::Master
24
24
 
25
25
  def start
26
- DTR.fork_process do
26
+ Process.fork do
27
27
  monitor
28
28
  end
29
29
  end
@@ -44,7 +44,7 @@ module DTR
44
44
  include Service::Agent
45
45
 
46
46
  def start
47
- DTR.fork_process do
47
+ Process.fork do
48
48
  begin
49
49
  loop do
50
50
  monitor
data/lib/dtr/raketasks.rb CHANGED
@@ -18,6 +18,7 @@ require 'rake/testtask'
18
18
  require 'rake/tasklib'
19
19
 
20
20
  require 'dtr'
21
+ require 'dtr/shared/root'
21
22
  require 'dtr/shared/ruby_ext'
22
23
  require 'dtr/shared/utils'
23
24
  require 'dtr/shared/sync_codebase/package'
@@ -85,23 +86,17 @@ module DTR
85
86
 
86
87
  desc "Run tests with DTR injected"
87
88
  task @name do
88
- @agent = start_agent
89
+ start_agent
89
90
  run_code = ''
90
- begin
91
- RakeFileUtils.verbose(@verbose) do
92
- run_code = rake_loader
93
- @ruby_opts.unshift( "-I#{lib_path}" )
94
- @ruby_opts.unshift( "-w" ) if @warning
95
-
96
- ruby @ruby_opts.join(" ") +
97
- " \"#{run_code}\" " +
98
- file_list.unshift('dtr/test_unit_injection.rb').collect { |fn| "\"#{fn}\"" }.join(' ') +
99
- " #{option_list}"
100
- end
101
- ensure
102
- if defined?(@agent)
103
- DTR.kill_process @agent
104
- end
91
+ RakeFileUtils.verbose(@verbose) do
92
+ run_code = rake_loader
93
+ @ruby_opts.unshift( "-I#{lib_path}" )
94
+ @ruby_opts.unshift( "-w" ) if @warning
95
+
96
+ ruby @ruby_opts.join(" ") +
97
+ " \"#{run_code}\" " +
98
+ file_list.unshift('dtr/test_unit_injection.rb').collect { |fn| "\"#{fn}\"" }.join(' ') +
99
+ " #{option_list}"
105
100
  end
106
101
  end
107
102
  self
@@ -113,9 +108,9 @@ module DTR
113
108
  runner_names = []
114
109
  self.processes.to_i.times {|i| runner_names << "runner#{i}"}
115
110
 
116
- DTR.fork_process do
117
- DTR_AGENT_OPTIONS[:runners] = runner_names if DTR_AGENT_OPTIONS[:runners].empty?
118
- DTR.start_agent
111
+ DTR.agent_runners = runner_names if DTR.agent_runners.blank?
112
+ Thread.start do
113
+ DTR.run_script('DTR::Agent.start(:hypnotize_once)')
119
114
  end
120
115
  end
121
116
  end
@@ -0,0 +1,118 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'timeout'
16
+
17
+ module DTR
18
+ module Adapter
19
+ WAKEUP_MESSAGE = 'wakeup'
20
+ SLEEP_MESSAGE = 'sleep'
21
+
22
+ module Follower
23
+ def wakeup?
24
+ msg, host, group = listen
25
+ if group == DTR.configuration.group && msg == Adapter::WAKEUP_MESSAGE
26
+ ip, port = host.split(':')
27
+ DTR.configuration.rinda_server_port = port.to_i
28
+ DTR.configuration.broadcast_list = [ip]
29
+ DTR.configuration.save
30
+ DTR.info {"=> Rinda server port: #{DTR.configuration.rinda_server_port}"}
31
+ DTR.info {"=> Broadcast list: #{DTR.configuration.broadcast_list.inspect}"}
32
+ @wakeup_for_host = host
33
+ true
34
+ end
35
+ end
36
+
37
+ def sleep?
38
+ return true unless defined?(@wakeup_for_host)
39
+ Timeout.timeout(DTR.configuration.follower_listen_heartbeat_timeout) do
40
+ until (msg, host = listen) && host == @wakeup_for_host; end
41
+ msg == Adapter::SLEEP_MESSAGE
42
+ end
43
+ rescue Timeout::Error => e
44
+ DTR.info {"Timeout while listening command"}
45
+ true
46
+ end
47
+
48
+ def relax
49
+ if defined?(@soc) && @soc
50
+ @soc.close rescue nil
51
+ end
52
+ end
53
+
54
+ private
55
+ def listen
56
+ unless defined?(@soc)
57
+ @soc = UDPSocket.open
58
+ @soc.bind('', DTR.configuration.agent_listen_port)
59
+ DTR.info("DTR Agent is listening on port #{DTR.configuration.agent_listen_port}")
60
+ end
61
+ message, client_address = @soc.recvfrom(400)
62
+ cmd, port, group = message.split
63
+
64
+ hostname = client_address[2]
65
+ host_ip = client_address[3]
66
+ DTR.info {"Received: #{cmd} for group #{group} from #{hostname}(#{host_ip}):#{port}"}
67
+ [cmd, "#{host_ip}:#{port}", group]
68
+ end
69
+ end
70
+
71
+ module Master
72
+ def hypnotize_agents
73
+ yell_agents("#{Adapter::SLEEP_MESSAGE} #{DTR.configuration.rinda_server_port}")
74
+ end
75
+
76
+ def with_wakeup_agents(&block)
77
+ heartbeat = Thread.new do
78
+ loop do
79
+ do_wakeup_agents
80
+ sleep(DTR.configuration.master_heartbeat_interval)
81
+ end
82
+ end
83
+ #heartbeat thread should have high priority for agent is listening
84
+ heartbeat.priority = Thread.current.priority + 10
85
+ block.call
86
+ ensure
87
+ #kill heartbeat first, so that agents wouldn't be wakeup after hypnotized
88
+ Thread.kill heartbeat rescue nil
89
+ hypnotize_agents rescue nil
90
+ end
91
+
92
+ def do_wakeup_agents
93
+ yell_agents("#{Adapter::WAKEUP_MESSAGE} #{DTR.configuration.rinda_server_port} #{DTR.configuration.group}")
94
+ end
95
+
96
+ private
97
+ def yell_agents(msg)
98
+ DTR.info {"yell agents #{msg}: #{DTR.configuration.broadcast_list.inspect}"}
99
+ DTR.configuration.broadcast_list.each do |it|
100
+ broadcast(it, msg)
101
+ end
102
+ end
103
+
104
+ def broadcast(it, msg)
105
+ soc = UDPSocket.open
106
+ begin
107
+ soc.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
108
+ DTR.debug {"broadcast sending #{msg} to #{it}"}
109
+ soc.send(msg, 0, it, DTR.configuration.agent_listen_port)
110
+ rescue
111
+ nil
112
+ ensure
113
+ soc.close
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'timeout'
16
+
17
+ module DTR
18
+ module Adapter
19
+ DEAD_MESSAGE = 'die'
20
+
21
+ module Follower
22
+ private
23
+
24
+ def listen_with_dead_message
25
+ cmd, host, group = listen_without_dead_message
26
+ if cmd == Adapter::DEAD_MESSAGE && group == DTR.configuration.group
27
+ DTR.info{"Master need me die. So I am dieing."}
28
+ raise Interrupt, "Master need me die."
29
+ end
30
+ [cmd, host, group]
31
+ end
32
+ alias_method_chain :listen, :dead_message
33
+ end
34
+
35
+ module Master
36
+ def group_agents_should_die(group = DTR.configuration.group)
37
+ yell_agents("#{Adapter::DEAD_MESSAGE} #{DTR.configuration.rinda_server_port} #{group}")
38
+ end
39
+ end
40
+ end
41
+ end