deep_test_pre 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +47 -0
  2. data/README.rdoc +199 -0
  3. data/Rakefile +137 -0
  4. data/lib/deep_test.rb +78 -0
  5. data/lib/deep_test/agent.rb +108 -0
  6. data/lib/deep_test/central_command.rb +165 -0
  7. data/lib/deep_test/cpu_info.rb +22 -0
  8. data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
  9. data/lib/deep_test/database/setup_listener.rb +116 -0
  10. data/lib/deep_test/deadlock_detector.rb +7 -0
  11. data/lib/deep_test/demon.rb +25 -0
  12. data/lib/deep_test/distributed/beachhead.rb +104 -0
  13. data/lib/deep_test/distributed/dispatch_controller.rb +60 -0
  14. data/lib/deep_test/distributed/establish_beachhead.rb +19 -0
  15. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  16. data/lib/deep_test/distributed/landing_fleet.rb +30 -0
  17. data/lib/deep_test/distributed/landing_ship.rb +60 -0
  18. data/lib/deep_test/distributed/remote_deployment.rb +56 -0
  19. data/lib/deep_test/distributed/rsync.rb +50 -0
  20. data/lib/deep_test/distributed/shell_environment.rb +50 -0
  21. data/lib/deep_test/distributed/ssh_client_connection_info.rb +14 -0
  22. data/lib/deep_test/extensions/object_extension.rb +40 -0
  23. data/lib/deep_test/failure_message.rb +19 -0
  24. data/lib/deep_test/lib_root.rb +4 -0
  25. data/lib/deep_test/listener_list.rb +17 -0
  26. data/lib/deep_test/local_deployment.rb +46 -0
  27. data/lib/deep_test/logger.rb +32 -0
  28. data/lib/deep_test/main.rb +41 -0
  29. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  30. data/lib/deep_test/metrics/data.rb +34 -0
  31. data/lib/deep_test/metrics/measurement.rb +39 -0
  32. data/lib/deep_test/null_listener.rb +62 -0
  33. data/lib/deep_test/options.rb +113 -0
  34. data/lib/deep_test/proxy_io.rb +77 -0
  35. data/lib/deep_test/rake_tasks.rb +13 -0
  36. data/lib/deep_test/result_reader.rb +40 -0
  37. data/lib/deep_test/rspec_detector.rb +21 -0
  38. data/lib/deep_test/spec.rb +17 -0
  39. data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
  40. data/lib/deep_test/spec/extensions/example_methods.rb +52 -0
  41. data/lib/deep_test/spec/extensions/options.rb +43 -0
  42. data/lib/deep_test/spec/extensions/spec_task.rb +21 -0
  43. data/lib/deep_test/spec/runner.rb +72 -0
  44. data/lib/deep_test/spec/work_result.rb +35 -0
  45. data/lib/deep_test/spec/work_unit.rb +59 -0
  46. data/lib/deep_test/test.rb +10 -0
  47. data/lib/deep_test/test/extensions/error.rb +14 -0
  48. data/lib/deep_test/test/run_test_suite.rb +5 -0
  49. data/lib/deep_test/test/runner.rb +24 -0
  50. data/lib/deep_test/test/supervised_test_suite.rb +48 -0
  51. data/lib/deep_test/test/work_result.rb +35 -0
  52. data/lib/deep_test/test/work_unit.rb +40 -0
  53. data/lib/deep_test/test_task.rb +47 -0
  54. data/lib/deep_test/ui/console.rb +74 -0
  55. data/lib/deep_test/ui/null.rb +17 -0
  56. data/lib/deep_test/warlock.rb +146 -0
  57. data/lib/telegraph.rb +29 -0
  58. data/lib/telegraph/ack_sequence.rb +14 -0
  59. data/lib/telegraph/logging.rb +20 -0
  60. data/lib/telegraph/message.rb +39 -0
  61. data/lib/telegraph/operator.rb +47 -0
  62. data/lib/telegraph/switchboard.rb +57 -0
  63. data/lib/telegraph/wire.rb +73 -0
  64. data/test/deep_test/agent_test.rb +175 -0
  65. data/test/deep_test/central_command_test.rb +147 -0
  66. data/test/deep_test/cpu_info_test.rb +33 -0
  67. data/test/deep_test/database/mysql_setup_listener_test.rb +18 -0
  68. data/test/deep_test/demon_test.rb +23 -0
  69. data/test/deep_test/distributed/beachhead_test.rb +67 -0
  70. data/test/deep_test/distributed/dispatch_controller_test.rb +162 -0
  71. data/test/deep_test/distributed/filename_resolver_test.rb +56 -0
  72. data/test/deep_test/distributed/landing_fleet_test.rb +55 -0
  73. data/test/deep_test/distributed/landing_ship_test.rb +48 -0
  74. data/test/deep_test/distributed/remote_deployment_test.rb +134 -0
  75. data/test/deep_test/distributed/rsync_test.rb +47 -0
  76. data/test/deep_test/distributed/shell_environment_test.rb +108 -0
  77. data/test/deep_test/distributed/ssh_client_connection_info_test.rb +34 -0
  78. data/test/deep_test/extensions/object_extension_test.rb +37 -0
  79. data/test/deep_test/listener_list_test.rb +22 -0
  80. data/test/deep_test/local_deployment_test.rb +19 -0
  81. data/test/deep_test/logger_test.rb +38 -0
  82. data/test/deep_test/main_test.rb +12 -0
  83. data/test/deep_test/marshallable_exception_wrapper_test.rb +46 -0
  84. data/test/deep_test/metrics/data_test.rb +22 -0
  85. data/test/deep_test/metrics/measurement_test.rb +18 -0
  86. data/test/deep_test/proxy_io_test.rb +104 -0
  87. data/test/deep_test/result_reader_test.rb +128 -0
  88. data/test/deep_test/test/extensions/error_test.rb +42 -0
  89. data/test/deep_test/test/runner_test.rb +11 -0
  90. data/test/deep_test/test/supervised_test_suite_test.rb +107 -0
  91. data/test/deep_test/test/work_result_test.rb +85 -0
  92. data/test/deep_test/test/work_unit_test.rb +63 -0
  93. data/test/deep_test/test_task_test.rb +15 -0
  94. data/test/deep_test/ui/console_test.rb +13 -0
  95. data/test/deep_test/warlock_test.rb +40 -0
  96. data/test/test_helper.rb +30 -0
  97. data/test/test_task_test.rb +75 -0
  98. metadata +156 -0
@@ -0,0 +1,7 @@
1
+ module DeepTest
2
+ class DeadlockDetector
3
+ def self.due_to_deadlock?(error)
4
+ error && !error.message.to_s.match(/Deadlock found when trying to get lock/).nil?
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ module DeepTest
2
+ module Demon
3
+ def forked(name, options, demon_args)
4
+ options.connect_to_central_command do |wire|
5
+ ProxyIO.replace_stdout_stderr!(wire) do
6
+ begin
7
+ catch(:exit_demon) do
8
+ Signal.trap("TERM") { throw :exit_demon }
9
+ execute *demon_args
10
+ end
11
+ rescue SystemExit => e
12
+ raise
13
+ rescue Exception => e
14
+ FailureMessage.show self.class.name, "Process #{Process.pid} exiting with excetion: #{e.class}: #{e.message}"
15
+ raise
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def execute(*args)
22
+ raise "#{self.class} must implement the execute method to be a Demon"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,104 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class Beachhead < LocalDeployment
4
+ include Demon
5
+
6
+ MERCY_KILLING_GRACE_PERIOD = 10 * 60 unless defined?(MERCY_KILLING_GRACE_PERIOD)
7
+
8
+ def initialize(base_path, options)
9
+ super options
10
+ @base_path = base_path
11
+ end
12
+
13
+ def launch_mercy_killer(grace_period)
14
+ Thread.new do
15
+ sleep grace_period
16
+ exit(0) unless agents_deployed?
17
+ end
18
+ end
19
+
20
+ def load_files(files)
21
+ spec_support_path = File.expand_path(File.dirname(__FILE__) + "/../spec")
22
+ Dir.chdir @base_path
23
+ resolver = FilenameResolver.new(@base_path)
24
+ files.each do |file|
25
+ load resolver.resolve(file)
26
+ end
27
+
28
+ # Load rspec support if rspec is available now that we've loaded the host project files
29
+ #
30
+ DeepTest::RSpecDetector.if_rspec_available do
31
+ require spec_support_path
32
+ end
33
+ end
34
+
35
+ def deploy_agents
36
+ @agents_deployed = true
37
+ super
38
+ warlock.exit_when_none_running
39
+ end
40
+
41
+ def agents_deployed?
42
+ @agents_deployed
43
+ end
44
+
45
+ def forked(*args)
46
+ $stdout.reopen("/dev/null")
47
+ $stderr.reopen("/dev/null")
48
+ super
49
+ end
50
+
51
+ def execute(innie, outie, grace_period)
52
+ innie.close
53
+
54
+ switchboard = Telegraph::Switchboard.new
55
+ operator = Telegraph::Operator.listen "0.0.0.0", 0, switchboard
56
+
57
+ DeepTest.logger.debug { "Beachhead started on port #{operator.port}" }
58
+
59
+ outie.write operator.port
60
+ outie.close
61
+
62
+ launch_mercy_killer grace_period
63
+
64
+ loop do
65
+ begin
66
+ switchboard.process_messages :timeout => 1 do |message, wire|
67
+ case message.body
68
+ when LoadFiles
69
+ load_files message.body.files
70
+ when DeployAgents
71
+ deploy_agents
72
+ wire.send_message Done
73
+ operator.shutdown
74
+ break
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def daemonize(grace_period = MERCY_KILLING_GRACE_PERIOD)
82
+ innie, outie = IO.pipe
83
+
84
+ warlock.start "Beachhead", self, innie, outie, grace_period
85
+
86
+ outie.close
87
+ port = innie.gets
88
+ innie.close
89
+ port.to_i
90
+ end
91
+
92
+ unless defined? DeployAgents
93
+ DeployAgents = "DeployAgents"
94
+ Done = "Done"
95
+ class LoadFiles
96
+ attr_reader :files
97
+ def initialize(files)
98
+ @files = files
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,60 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class DispatchController
4
+ def initialize(options, receivers)
5
+ @options = options
6
+ @receivers = receivers
7
+ end
8
+
9
+ def dispatch(method_name, *args)
10
+ dispatch_with_options(method_name, {}, *args)
11
+ end
12
+
13
+ def dispatch_with_options(method_name, options, *args)
14
+ raise NoDispatchReceiversError if @receivers.empty?
15
+
16
+ @options.ui_instance.dispatch_starting(method_name)
17
+
18
+ threads = @receivers.map do |r|
19
+ Thread.new do
20
+ begin
21
+ Thread.current[:receiver] = r
22
+ r.send method_name, *args
23
+ rescue Exception => e
24
+ Thread.current[:original_exception] = e
25
+ raise
26
+ end
27
+ end
28
+ end
29
+
30
+ results = []
31
+ threads.each do |t|
32
+ begin
33
+ results << t.value
34
+ rescue Timeout::Error
35
+ @receivers.delete t[:receiver]
36
+ DeepTest.logger.error { "Timeout dispatching #{method_name} to #{description t[:receiver]}" }
37
+ rescue Exception => this_exception
38
+ @receivers.delete t[:receiver]
39
+ DeepTest.logger.error { "Exception while dispatching #{method_name} to #{description t[:receiver]}:" }
40
+
41
+ e = t[:original_exception] || this_exception
42
+ DeepTest.logger.error { "#{e.class}: #{e.message}" }
43
+ e.backtrace.each {|l| DeepTest.logger.error { l } }
44
+ end
45
+ end
46
+
47
+ results
48
+ ensure
49
+ @options.ui_instance.dispatch_finished(method_name)
50
+ end
51
+
52
+ def description(receiver)
53
+ receiver.inspect
54
+ end
55
+
56
+ end
57
+
58
+ class NoDispatchReceiversError < StandardError; end
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + "/../../deep_test"
2
+ options = DeepTest::Options.from_command_line(ENV['OPTIONS'])
3
+ ENV['DEEP_TEST_LOG_LEVEL'] = options.environment_log_level
4
+ options.ssh_client_connection_info = DeepTest::Distributed::SshClientConnectionInfo.new
5
+
6
+ DeepTest.logger.debug { "mirror establish_beachhead for #{options.origin_hostname}" }
7
+
8
+ STDIN.close
9
+
10
+ beachhead_port = DeepTest::Distributed::Beachhead.new(
11
+ File.join(options.mirror_path, File.basename(options.sync_options[:source])),
12
+ options
13
+ ).daemonize
14
+
15
+ puts "Beachhead port: #{beachhead_port}"
16
+ $stdout.flush
17
+
18
+ exit!(0)
19
+
@@ -0,0 +1,40 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class FilenameResolver
4
+ def initialize(base_path)
5
+ @base_path = base_path
6
+ end
7
+
8
+ def resolve(filename)
9
+ return resolve("/" + filename) unless filename[0] == ?/
10
+
11
+ return filename.sub(@cached_replaced_path, @base_path) if @cached_replaced_path
12
+
13
+ each_potential_filename(filename) do |potential_filename|
14
+ if File.exist?(potential_filename)
15
+ cache_resolution(filename, potential_filename)
16
+ return potential_filename
17
+ end
18
+ end
19
+
20
+ raise "Filename resolution failed. Cannot resolve #{filename} within #{@base_path}"
21
+ end
22
+
23
+ def cache_resolution(original_filename, resolved_filename)
24
+ @cached_replaced_path = original_filename.sub(
25
+ resolved_filename.sub(@base_path, ""), ""
26
+ )
27
+ end
28
+
29
+ def each_potential_filename(filename)
30
+ basename = File.basename(filename)
31
+ dirs = File.dirname(filename).split('/')
32
+
33
+ begin
34
+ dirs.shift
35
+ yield [@base_path, dirs, basename].flatten.join('/')
36
+ end until dirs.empty?
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class LandingFleet
4
+ def initialize(options, slaves)
5
+ DeepTest.logger.debug { "LandingFleet#initialize #{slaves.length} slaves" }
6
+ @slave_controller = DispatchController.new(options, slaves)
7
+ end
8
+
9
+ def establish_beachhead(options)
10
+ DeepTest.logger.debug { "dispatch establish_beachhead for #{options.origin_hostname}" }
11
+ @slave_controller.dispatch :establish_beachhead, options
12
+ end
13
+
14
+ def push_code(options)
15
+ DeepTest.logger.debug { "dispatch push_code for #{options.origin_hostname}" }
16
+ @slave_controller.dispatch :push_code, options
17
+ end
18
+
19
+ def load_files(files)
20
+ DeepTest.logger.debug { "dispatch load_files" }
21
+ @slave_controller.dispatch :load_files, files
22
+ end
23
+
24
+ def deploy_agents
25
+ DeepTest.logger.debug { "dispatch deploy_agents" }
26
+ @slave_controller.dispatch :deploy_agents
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class LandingShip
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def push_code(options)
9
+ RSync.push(@config[:address], options.sync_options, options.mirror_path)
10
+ end
11
+
12
+ def establish_beachhead(options)
13
+ command = "#{ssh_command(options)} '#{spawn_command(options)}' 2>&1"
14
+ DeepTest.logger.debug { "Establishing Beachhead: #{command}" }
15
+
16
+ output = `#{command}`
17
+ output.each do |line|
18
+ if DeepTest.logger.level == Logger::DEBUG
19
+ puts output
20
+ end
21
+ if line =~ /Beachhead port: (.+)/
22
+ @wire = Telegraph::Wire.connect(@config[:address], $1.to_i)
23
+ end
24
+ end
25
+ raise "LandingShip unable to establish Beachhead. Output from #{@config[:address]} was:\n#{output}" unless @wire
26
+ end
27
+
28
+ def load_files(files)
29
+ @wire.send_message Beachhead::LoadFiles.new(files)
30
+ end
31
+
32
+ def deploy_agents
33
+ @wire.send_message Beachhead::DeployAgents
34
+ begin
35
+ message = @wire.next_message :timeout => 1
36
+ raise "Unexpected message from Beachhead: #{message.inspect}" unless message.body == Beachhead::Done
37
+ rescue Telegraph::NoMessageAvailable
38
+ retry
39
+ end
40
+ end
41
+
42
+ def ssh_command(options)
43
+ username_option = if options.sync_options[:username]
44
+ " -l #{options.sync_options[:username]}"
45
+ else
46
+ ""
47
+ end
48
+
49
+ "ssh -4 #{@config[:address]}#{username_option}"
50
+ end
51
+
52
+ def spawn_command(options)
53
+ "#{ShellEnvironment.like_login} && " +
54
+ "cd #{options.mirror_path} && " +
55
+ "OPTIONS=#{options.to_command_line} " +
56
+ "ruby lib/deep_test/distributed/establish_beachhead.rb"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,56 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RemoteDeployment
4
+ def initialize(options, landing_fleet, failover_deployment)
5
+ @failover_deployment = failover_deployment
6
+ @options = options
7
+ @landing_fleet = landing_fleet
8
+ end
9
+
10
+ def load_files(filelist)
11
+ # load one file before calling listeners to make sure environment has
12
+ # been initialized as expected
13
+ #
14
+ load filelist.first
15
+ @options.new_listener_list.before_sync
16
+
17
+ t = Thread.new do
18
+ @landing_fleet.push_code(@options)
19
+ @landing_fleet.establish_beachhead(@options)
20
+ @landing_fleet.load_files filelist
21
+ end
22
+
23
+ filelist[1..-1].each {|f| load f}
24
+
25
+ begin
26
+ t.join
27
+ rescue => e
28
+ # The failover here doesn't invoke load_files on the failover_deployment
29
+ # because it will be a LocalDeployment, which forks from the current
30
+ # process. The fact that we depend in this here is damp...
31
+ #
32
+ fail_over("load_files", e)
33
+ end
34
+ end
35
+
36
+ def deploy_agents
37
+ DeepTest.logger.debug { "RemoteDeployment deploying agents with #{@landing_fleet}" }
38
+ @landing_fleet.deploy_agents
39
+ rescue => e
40
+ raise if failed_over?
41
+ fail_over("deploy_agents", e)
42
+ retry
43
+ end
44
+
45
+ def fail_over(method, exception)
46
+ DeepTest.logger.debug { "RemoteDeployment failing over on #{method}." }
47
+ @options.ui_instance.distributed_failover_to_local(method, exception)
48
+ @landing_fleet = @failover_deployment
49
+ end
50
+
51
+ def failed_over?
52
+ @landing_fleet == @failover_deployment
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,50 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RSync
4
+ def self.push(address, sync_options, destination)
5
+ sync(:push, address, sync_options, destination)
6
+ end
7
+
8
+ def self.sync(operation, address, sync_options, destination)
9
+ command = Args.new(address, sync_options).send("#{operation}_command",
10
+ destination)
11
+
12
+ DeepTest.logger.debug { "rsycing: #{command}" }
13
+ successful = system command
14
+ raise "RSync Failed!!" unless successful
15
+ end
16
+
17
+ class Args
18
+ def initialize(address, sync_options)
19
+ @address = address
20
+ @sync_options = sync_options
21
+ end
22
+
23
+ def pull_command(destination)
24
+ command remote_location(@sync_options[:source]), destination
25
+ end
26
+
27
+ def push_command(destination)
28
+ command @sync_options[:source], remote_location(destination)
29
+ end
30
+
31
+ def command(source, destination)
32
+ # The '/' after source tells rsync to copy the contents
33
+ # of source to destination, rather than the source directory
34
+ # itself
35
+ "rsync -az --delete #{@sync_options[:rsync_options]} #{DeepTest::LIB_ROOT} #{source} #{destination}".strip.squeeze(" ")
36
+ end
37
+
38
+ def remote_location(path)
39
+ source = ""
40
+ unless @sync_options[:local]
41
+ source << @sync_options[:username] << '@' if @sync_options[:username]
42
+ source << @address
43
+ source << (@sync_options[:daemon] ? '::' : ':')
44
+ end
45
+ source << path
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end