tddium-old 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,212 @@
1
+ =begin
2
+ Copyright (c) 2010 tddium.com All Rights Reserved
3
+ =end
4
+
5
+ #
6
+ # tddium support methods
7
+ #
8
+ #
9
+
10
+ require 'rubygems'
11
+ require 'fog'
12
+ require 'net/http'
13
+ require 'uri'
14
+ AMI_NAME = 'ami-da13e3b3'
15
+ DEV_SESSION_KEY='dev'
16
+
17
+ def setup_environment(server)
18
+ if !$tunnel_pid.nil?
19
+ ENV['SELENIUM_RC_HOST'] = 'localhost'
20
+ else
21
+ ENV['SELENIUM_RC_HOST'] = server.dns_name
22
+ end
23
+ ENV['TDDIUM'] = '1'
24
+ end
25
+
26
+ # Start and setup an EC2 instance to run a selenium-grid node. Set the
27
+ # tddium_session tag to session_key, if it's specified.
28
+ def start_instance(session_key=nil)
29
+ conf = read_config
30
+
31
+ if session_key.nil?
32
+ @tddium_session = rand(2**64-1).to_s(36)
33
+ else
34
+ @tddium_session = session_key
35
+ end
36
+
37
+ key_file = get_keyfile
38
+
39
+ @ec2pool = Fog::AWS::Compute.new(:aws_access_key_id => conf[:aws_key],
40
+ :aws_secret_access_key => conf[:aws_secret])
41
+
42
+ server = @ec2pool.servers.create(:flavor_id => 'm1.large',
43
+ :groups => ['selenium-grid'],
44
+ :image_id => AMI_NAME,
45
+ :name => 'sg-server',
46
+ :key_name => conf[:key_name])
47
+
48
+ server.wait_for { ready? }
49
+ server.reload
50
+
51
+ @ec2pool.tags.create(:key => 'tddium_session',
52
+ :value => @tddium_session,
53
+ :resource_id => server.id)
54
+
55
+ if conf.include?(:server_tag) then
56
+ server_tag = conf[:server_tag].split('=')
57
+
58
+ @ec2pool.tags.create(:key => server_tag[0],
59
+ :value => server_tag[1],
60
+ :resource_id => server.id)
61
+ end
62
+
63
+
64
+ puts "started instance #{server.id} #{server.dns_name} in group #{server.groups} with tags #{server.tags.inspect}"
65
+
66
+ if conf[:ssh_tunnel] == "true"
67
+ make_ssh_tunnel(key_file, server)
68
+ end
69
+
70
+ setup_environment(server)
71
+
72
+ uri = wait_for_selenium(ENV['SELENIUM_RC_HOST'])
73
+
74
+ puts "Selenium Console:"
75
+ puts "#{uri}"
76
+
77
+ if !key_file.nil?
78
+ STDERR.puts "You can login via \"ssh -i #{key_file} ec2-user@#{server.dns_name}\""
79
+ STDERR.puts "Making /var/log/messages world readable"
80
+ remote_cmd(server.dns_name, "sudo chmod 644 /var/log/messages")
81
+ else
82
+ # TODO: Remove when /var/log/messages bug is fixed
83
+ STDERR.puts "No key_file provided. /var/log/messages may not be readable by ec2-user."
84
+ end
85
+
86
+ server
87
+ end
88
+
89
+ def wait_for_selenium(hostname)
90
+ uri = URI.parse("http://#{hostname}:4444/console")
91
+ http = Net::HTTP.new(uri.host, uri.port)
92
+ http.open_timeout = 60
93
+ http.read_timeout = 60
94
+
95
+ rc_up = false
96
+ tries = 0
97
+ while !rc_up && tries < 5
98
+ begin
99
+ http.request(Net::HTTP::Get.new(uri.request_uri))
100
+ rc_up = true
101
+ rescue Errno::ECONNREFUSED
102
+ sleep 5
103
+ rescue Timeout::Error
104
+ ensure
105
+ tries += 1
106
+ end
107
+ end
108
+ raise "Couldn't connect to #{uri.request_uri}" unless rc_up
109
+ uri
110
+ end
111
+
112
+ def recycle_server(dns_name)
113
+ conf = read_config
114
+ keyfile = get_keyfile
115
+
116
+ if keyfile.nil?
117
+ raise "No keyfile. Can't execute remote commands"
118
+ end
119
+
120
+ remote_cmd(dns_name, "sudo killall -9 java")
121
+ remote_cmd(dns_name, "bin/launch-hub.sh")
122
+ wait_for_selenium(dns_name)
123
+ remote_cmd(dns_name, "bin/launch-rc.sh")
124
+ sleep 5
125
+ STDERR.puts "Recycled Selenium on instance: #{dns_name}"
126
+ end
127
+
128
+ def recycle_servers
129
+ servers = find_instances
130
+ servers.each do |s|
131
+ recycle_server(s.dns_name)
132
+ end
133
+ end
134
+
135
+ def recycle_dev
136
+ dev_servers = session_instances(DEV_SESSION_KEY)
137
+
138
+ if dev_servers.length > 0 then
139
+ recycle_server(dev_servers[0].dns_name)
140
+ else
141
+ STDERR.puts "No dev servers found"
142
+ end
143
+ end
144
+
145
+ def checkstart_dev_instance
146
+ conf = read_config
147
+ dev_servers = session_instances(DEV_SESSION_KEY)
148
+ if dev_servers.length > 0
149
+ STDERR.puts "Using existing server #{dev_servers[0].dns_name}."
150
+ setup_environment(dev_servers[0])
151
+ return dev_servers[0]
152
+ else
153
+ STDERR.puts "Starting EC2 Instance"
154
+ server = start_instance(DEV_SESSION_KEY)
155
+ sleep 30
156
+ return server
157
+ end
158
+ end
159
+
160
+ # Find all instances running the tddium AMI
161
+ def find_instances
162
+ conf = read_config
163
+ @ec2pool = Fog::AWS::Compute.new(:aws_access_key_id => conf[:aws_key],
164
+ :aws_secret_access_key => conf[:aws_secret])
165
+
166
+ @ec2pool.servers.select do |s|
167
+ s.image_id == AMI_NAME &&
168
+ !%w{terminated stopped shutting-down}.include?(s.state)
169
+ end
170
+ end
171
+
172
+ def session_instances(session_key)
173
+ servers = find_instances
174
+ if servers.nil?
175
+ return nil
176
+ else
177
+ session_servers = []
178
+ servers.each do |s|
179
+ # in Fog 0.3.33, :filters is buggy and won't accept resourceId or resource_id
180
+ tags = @ec2pool.tags(:filters => {:key => 'tddium_session'}).select{|t| t.resource_id == s.id}
181
+ if tags.first and tags.first.value == session_key then
182
+ STDERR.puts "selecting instance #{s.id} #{s.dns_name} from our session"
183
+ session_servers << s
184
+ else
185
+ STDERR.puts "skipping instance #{s.id} #{s.dns_name} created in another session"
186
+ end
187
+ end
188
+ return session_servers
189
+ end
190
+ end
191
+
192
+ def stop_all_instances
193
+ servers = find_instances
194
+ servers.each do |s|
195
+ STDERR.puts "stopping instance #{s.id} #{s.dns_name}"
196
+ s.destroy
197
+ end
198
+ end
199
+
200
+ # Stop the instance created by start_instance
201
+ def stop_instance(session_key=nil)
202
+ conf = read_config
203
+
204
+ kill_tunnel
205
+
206
+ servers = session_instances(session_key ? session_key : @tddium_session)
207
+ servers.each do |s|
208
+ STDERR.puts "stopping instance #{s.id} #{s.dns_name} from our session"
209
+ s.destroy
210
+ end
211
+ nil
212
+ end
@@ -0,0 +1,87 @@
1
+ =begin
2
+ Copyright (c) 2010 tddium.com All Rights Reserved
3
+ =end
4
+
5
+ require 'parallel'
6
+ require 'tddium/config'
7
+
8
+ def make_spec_cmd(tests, environment, result_path)
9
+ if !result_path
10
+ raise "result_path must be specified"
11
+ end
12
+
13
+ environ = {
14
+ "RAILS_ENV" => environment,
15
+ 'RSPEC_COLOR' => $stdout.tty? ? 1 : nil
16
+ }
17
+
18
+ env_str = environ.map{|k,v| "#{k}=#{v}" if v}.join(' ')
19
+ cmd = "env #{env_str} "
20
+ cmd << "spec "
21
+ cmd << "#{tests.map{|x| "\"#{x}\""}.join(' ')} "
22
+ cmd << spec_opts(result_path).join(' ')
23
+ cmd
24
+ end
25
+
26
+ def test_batches(num_batches)
27
+ tests = find_test_files
28
+ STDERR.puts "\t#{tests.size} test files"
29
+
30
+ chunk_size = tests.size / num_batches
31
+ remainder = tests.size % num_batches
32
+ batches = []
33
+ num_batches.times do |c|
34
+ if c < remainder
35
+ batches << tests[((c*chunk_size)+c),(chunk_size+1)]
36
+ else
37
+ batches << tests[((c*chunk_size)+remainder),chunk_size]
38
+ end
39
+ end
40
+
41
+ STDERR.puts batches.inspect
42
+ batches
43
+ end
44
+
45
+ def parallel_task(args)
46
+ args.with_defaults(:threads => 5, :environment => "selenium", :result_directory => '.')
47
+ threads = args.threads.to_i
48
+
49
+ STDERR.puts args.inspect
50
+
51
+ latest = args.result_directory
52
+ puts "Running tests. Results will be in #{latest}"
53
+ puts "Started at #{Time.now.inspect}"
54
+
55
+ output = {}
56
+
57
+ batches = test_batches(threads)
58
+ num_threads = batches.size
59
+
60
+ Parallel.in_threads(num_threads) do |i|
61
+ if batches[i].size > 0
62
+ result_path = File.join(latest, "#{i}-#{REPORT_FILENAME}")
63
+ cmd = make_spec_cmd(batches[i], args.environment, result_path)
64
+ output.merge!({"#{batches[i].inspect}" => execute_command( cmd )})
65
+ #puts "Running results: #{output.inspect}"
66
+ end
67
+ end
68
+
69
+ puts "Results:"
70
+ output.each do |key, value|
71
+ puts ">>>>>>>> #{key}"
72
+ puts value
73
+ end
74
+ puts "Finished at #{Time.now.inspect}"
75
+ end
76
+
77
+ def self.execute_command(cmd)
78
+ STDERR.puts "Running '#{cmd}'"
79
+ f = open("|#{cmd}")
80
+ all = ''
81
+ while out = f.gets(".")
82
+ all+=out
83
+ print out
84
+ STDOUT.flush
85
+ end
86
+ all
87
+ end
@@ -0,0 +1,71 @@
1
+ =begin
2
+ Copyright (c) 2010 tddium.com All Rights Reserved
3
+ =end
4
+
5
+ require 'tddium/config'
6
+ require 'digest/sha1'
7
+ require 'tddium_helper'
8
+ require 'ftools'
9
+
10
+ # Portions of this file derived from spec_storm, under the following license:
11
+ #
12
+ # Copyright (c) 2010 Sauce Labs Inc
13
+ #
14
+ # Permission is hereby granted, free of charge, to any person obtaining
15
+ # a copy of this software and associated documentation files (the
16
+ # "Software"), to deal in the Software without restriction, including
17
+ # without limitation the rights to use, copy, modify, merge, publish,
18
+ # distribute, sublicense, and/or sell copies of the Software, and to
19
+ # permit persons to whom the Software is furnished to do so, subject to
20
+ # the following conditions:
21
+ #
22
+ # The above copyright notice and this permission notice shall be
23
+ # included in all copies or substantial portions of the Software.
24
+ #
25
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
+
33
+ def prepare_task(args)
34
+ args.with_defaults(:environment => "selenium")
35
+
36
+ tests = find_test_files()
37
+ puts "\t#{tests.size} test files"
38
+ first = true
39
+
40
+ tests.each do |test|
41
+ prefix = SpecStorm::db_prefix_for(test)
42
+ puts "Migrating another set of tables..."
43
+ puts "Generating DB_PREFIX: #{test} -> #{prefix}"
44
+
45
+ if first == true
46
+ ["export DB_PREFIX=#{prefix}; rake db:drop RAILS_ENV=#{args.environment} --trace",
47
+ "export DB_PREFIX=#{prefix}; rake db:create RAILS_ENV=#{args.environment} --trace"].each do |command|
48
+ IO.popen( command ).close
49
+ end
50
+ end
51
+
52
+ ["export DB_PREFIX=#{prefix}; rake db:migrate RAILS_ENV=#{args.environment} --trace"].each do |cmd|
53
+ IO.popen( cmd ).close
54
+ end
55
+
56
+ first = false
57
+ end
58
+ end
59
+
60
+ def setup_task(args)
61
+ args.with_defaults(:environment => "selenium")
62
+
63
+ File.copy(File.join(RAILS_ROOT, 'config', 'environments', 'test.rb'),
64
+ File.join(RAILS_ROOT, 'config', 'environments', "#{args.environment}.rb"))
65
+
66
+ open(File.join(RAILS_ROOT, 'config', 'environments', "#{args.environment}.rb"), 'a') do |f|
67
+ f.puts "\nmodule SpecStorm"
68
+ f.puts " USE_NAMESPACE_HACK = true"
69
+ f.puts "end"
70
+ end
71
+ end
@@ -0,0 +1,42 @@
1
+ =begin
2
+ Copyright (c) 2010 tddium.com All Rights Reserved
3
+ =end
4
+ #
5
+ # Prepare the result directory, as specified by config[:result_directory].
6
+ #
7
+ # If the directory doesn't exist create it, and a latest subdirectory.
8
+ #
9
+ # If the latest subdirectory exists, rotate it and create a new empty latest.
10
+ #
11
+ def result_directory
12
+ conf = read_config
13
+ latest = File.join(conf[:result_directory], 'latest')
14
+
15
+ if File.directory?(latest) then
16
+ mtime = File.stat(latest).mtime.strftime("%Y%m%d-%H%M%S")
17
+ archive = File.join(conf[:result_directory], mtime)
18
+ FileUtils.mv(latest, archive)
19
+ end
20
+ FileUtils.mkdir_p latest
21
+ latest
22
+ end
23
+
24
+ REPORT_FILENAME = "selenium_report.html"
25
+
26
+ def default_report_path
27
+ File.join(read_config[:result_directory], 'latest', REPORT_FILENAME)
28
+ end
29
+
30
+ def collect_syslog(target_directory='.')
31
+ keyfile = get_keyfile
32
+ if keyfile.nil?
33
+ raise "No ssh keyfile configured. Can't connect to remote"
34
+ end
35
+ instances = session_instances(@tddium_session ? @tddium_session : DEV_SESSION_KEY)
36
+ instances.each do |inst|
37
+ %w(selenium-hub selenium-rc).each do |log|
38
+ remote_cp(inst.dns_name, "/var/log/#{log}.log",
39
+ File.join(target_directory, "#{log}.#{inst.dns_name}"))
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ =begin
2
+ Copyright (c) 2010 tddium.com All Rights Reserved
3
+ =end
4
+
5
+ # Subprocess main body to create an ssh tunnel to hostname for selenium, binding
6
+ # remote:4444 to local:4444. Authenticate with the private key in key_file.
7
+ #
8
+ # The ssh tunnel will auto-accept the remote host key.
9
+ def ssh_tunnel(hostname)
10
+ ssh_up = false
11
+ tries = 0
12
+ while !ssh_up && tries < 3
13
+ sleep 3
14
+ ssh_up = remote_cmd(hostname, "-L 4444:#{hostname}:4444 -N")
15
+ tries += 1
16
+ end
17
+ end
18
+
19
+ def make_ssh_tunnel(key_file, server)
20
+ $tunnel_pid = nil
21
+ if !key_file.nil? then
22
+ $tunnel_pid = Process.fork do
23
+ ssh_tunnel(server.dns_name)
24
+ end
25
+
26
+ STDERR.puts "Created ssh tunnel to #{server.dns_name}:4444 at localhost:4444 [pid #{$tunnel_pid}]"
27
+ end
28
+ end
29
+
30
+ def kill_tunnel
31
+ if !$tunnel_pid.nil?
32
+ Process.kill("TERM", $tunnel_pid)
33
+ Process.waitpid($tunnel_pid)
34
+ $tunnel_pid = nil
35
+ end
36
+ end
37
+
38
+
39
+ def remote_cmd(host, cmd)
40
+ key_file = get_keyfile
41
+
42
+ system("ssh -o 'StrictHostKeyChecking no' -i #{key_file} ec2-user@#{host} '#{cmd}'")
43
+ end
44
+
45
+ def remote_cp(host, remote_file, local_file)
46
+ key_file = get_keyfile
47
+ system("scp -o 'StrictHostKeyChecking no' -i #{key_file} ec2-user@#{host}:#{remote_file} #{local_file}")
48
+ end
49
+