testbot 0.4.6 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/requester.rb DELETED
@@ -1,130 +0,0 @@
1
- require 'rubygems'
2
- require 'httparty'
3
- require 'macaddr'
4
- require 'ostruct'
5
- require File.dirname(__FILE__) + '/shared/ssh_tunnel'
6
- require File.dirname(__FILE__) + '/adapters/adapter'
7
- require File.expand_path(File.dirname(__FILE__) + '/testbot')
8
-
9
- class Hash
10
- def symbolize_keys_without_active_support
11
- inject({}) do |options, (key, value)|
12
- options[(key.to_sym rescue key) || key] = value
13
- options
14
- end
15
- end
16
- end
17
-
18
- class Requester
19
-
20
- attr_reader :config
21
-
22
- def initialize(config = {})
23
- config = config.symbolize_keys_without_active_support
24
- config[:rsync_path] ||= Testbot::DEFAULT_SERVER_PATH
25
- config[:project] ||= Testbot::DEFAULT_PROJECT
26
- config[:server_user] ||= Testbot::DEFAULT_USER
27
- config[:available_runner_usage] ||= Testbot::DEFAULT_RUNNER_USAGE
28
- @config = OpenStruct.new(config)
29
- end
30
-
31
- def run_tests(adapter, dir)
32
- puts if config.simple_output
33
-
34
- if config.ssh_tunnel
35
- SSHTunnel.new(config.server_host, config.server_user, adapter.requester_port).open
36
- server_uri = "http://127.0.0.1:#{adapter.requester_port}"
37
- else
38
- server_uri = "http://#{config.server_host}:#{Testbot::SERVER_PORT}"
39
- end
40
-
41
- rsync_ignores = config.rsync_ignores.to_s.split.map { |pattern| "--exclude='#{pattern}'" }.join(' ')
42
- system "rsync -az --delete -e ssh #{rsync_ignores} . #{rsync_uri}"
43
-
44
- files = adapter.test_files(dir)
45
- sizes = adapter.get_sizes(files)
46
-
47
- build_id = HTTParty.post("#{server_uri}/builds", :body => { :root => root,
48
- :type => adapter.type.to_s,
49
- :project => config.project,
50
- :requester_mac => Mac.addr,
51
- :available_runner_usage => config.available_runner_usage,
52
- :files => files.join(' '),
53
- :sizes => sizes.join(' '),
54
- :jruby => jruby? })
55
-
56
-
57
- last_results_size = 0
58
- success = true
59
- error_count = 0
60
- while true
61
- sleep 1
62
-
63
- begin
64
- @build = HTTParty.get("#{server_uri}/builds/#{build_id}", :format => :json)
65
- next unless @build
66
- rescue Exception => ex
67
- error_count += 1
68
- if error_count > 4
69
- puts "Failed to get status: #{ex.message}"
70
- error_count = 0
71
- end
72
- next
73
- end
74
-
75
- results = @build['results'][last_results_size..-1]
76
- unless results == ''
77
- if config.simple_output
78
- print results.gsub(/[^\.F]|Finished/, '')
79
- STDOUT.flush
80
- else
81
- puts results
82
- end
83
- end
84
-
85
- last_results_size = @build['results'].size
86
-
87
- break if @build['done']
88
- end
89
-
90
- puts if config.simple_output
91
-
92
- @build["success"]
93
- end
94
-
95
- def self.create_by_config(path)
96
- config = YAML.load_file(path)
97
- Requester.new(config)
98
- end
99
-
100
- def result_lines
101
- @build['results'].split("\n").find_all { |line| line_is_result?(line) }.map { |line| line.chomp }
102
- end
103
-
104
- private
105
-
106
- def root
107
- if localhost?
108
- config.rsync_path
109
- else
110
- "#{config.server_user}@#{config.server_host}:#{config.rsync_path}"
111
- end
112
- end
113
-
114
- def rsync_uri
115
- localhost? ? config.rsync_path : "#{config.server_user}@#{config.server_host}:#{config.rsync_path}"
116
- end
117
-
118
- def localhost?
119
- [ '0.0.0.0', 'localhost', '127.0.0.1' ].include?(config.server_host)
120
- end
121
-
122
- def line_is_result?(line)
123
- line =~ /\d+ fail/
124
- end
125
-
126
- def jruby?
127
- RUBY_PLATFORM =~ /java/ || !!ENV['USE_JRUBY']
128
- end
129
-
130
- end
data/lib/runner.rb DELETED
@@ -1,213 +0,0 @@
1
- require 'rubygems'
2
- require 'httparty'
3
- require 'macaddr'
4
- require 'ostruct'
5
- require File.dirname(__FILE__) + '/shared/ssh_tunnel'
6
- require File.dirname(__FILE__) + '/adapters/adapter'
7
- require File.dirname(__FILE__) + '/runner/job'
8
-
9
- TIME_BETWEEN_NORMAL_POLLS = 1
10
- TIME_BETWEEN_QUICK_POLLS = 0.1
11
- TIME_BETWEEN_PINGS = 5
12
- TIME_BETWEEN_VERSION_CHECKS = Testbot.version.include?('.DEV.') ? 10 : 60
13
- MAX_CPU_USAGE_WHEN_IDLE = 50
14
-
15
- class CPU
16
-
17
- def self.current_usage
18
- process_usages = `ps -eo pcpu`
19
- total_usage = process_usages.split("\n").inject(0) { |sum, usage| sum += usage.strip.to_f }
20
- (total_usage / count).to_i
21
- end
22
-
23
- def self.count
24
- case RUBY_PLATFORM
25
- when /darwin/
26
- `hwprefs cpu_count`.to_i
27
- when /linux/
28
- `cat /proc/cpuinfo | grep processor | wc -l`.to_i
29
- end
30
- end
31
-
32
- end
33
-
34
- class Server
35
- include HTTParty
36
- end
37
-
38
- class Runner
39
-
40
- def initialize(config)
41
- @instances = []
42
- @last_requester_mac = nil
43
- @last_version_check = Time.now - TIME_BETWEEN_VERSION_CHECKS - 1
44
- @config = OpenStruct.new(config)
45
- @config.max_instances = @config.max_instances ? @config.max_instances.to_i : CPU.count
46
-
47
- if @config.ssh_tunnel
48
- server_uri = "http://127.0.0.1:#{Testbot::SERVER_PORT}"
49
- else
50
- server_uri = "http://#{@config.server_host}:#{Testbot::SERVER_PORT}"
51
- end
52
-
53
- Server.base_uri(server_uri)
54
- end
55
-
56
- attr_reader :config
57
-
58
- def run!
59
- # Remove legacy instance* and *_rsync|git style folders
60
- Dir.entries(".").find_all { |name| name.include?('instance') || name.include?('_rsync') ||
61
- name.include?('_git') }.each { |folder|
62
- system "rm -rf #{folder}"
63
- }
64
-
65
- SSHTunnel.new(@config.server_host, @config.server_user || Testbot::DEFAULT_USER).open if @config.ssh_tunnel
66
- while true
67
- begin
68
- update_uid!
69
- start_ping
70
- wait_for_jobs
71
- rescue Exception => ex
72
- break if [ 'SignalException', 'Interrupt' ].include?(ex.class.to_s)
73
- puts "The runner crashed, restarting. Error: #{ex.inspect} #{ex.class}"
74
- end
75
- end
76
- end
77
-
78
- private
79
-
80
- def update_uid!
81
- # When a runner crashes or is restarted it might loose current job info. Because
82
- # of this we provide a new unique ID to the server so that it does not wait for
83
- # lost jobs to complete.
84
- @uid = "#{Time.now.to_i}@#{Mac.addr}"
85
- end
86
-
87
- def wait_for_jobs
88
- last_check_found_a_job = false
89
- loop do
90
- sleep (last_check_found_a_job ? TIME_BETWEEN_QUICK_POLLS : TIME_BETWEEN_NORMAL_POLLS)
91
-
92
- check_for_update if !last_check_found_a_job && time_for_update?
93
-
94
- # Only get jobs from one requester at a time
95
- next_params = base_params
96
- if @instances.size > 0
97
- next_params.merge!({ :requester_mac => @last_requester_mac })
98
- next_params.merge!({ :no_jruby => true }) if max_jruby_instances?
99
- else
100
- @last_requester_mac = nil
101
- end
102
-
103
- # Makes sure all instances are listed as available after a run
104
- clear_completed_instances
105
- next unless cpu_available?
106
-
107
- next_job = Server.get("/jobs/next", :query => next_params) rescue nil
108
- last_check_found_a_job = (next_job != nil)
109
- next unless last_check_found_a_job
110
-
111
- job = Job.new(*([ self, next_job.split(',') ].flatten))
112
- if first_job_from_requester?
113
- fetch_code(job)
114
- before_run(job) if File.exists?("#{job.project}/lib/tasks/testbot.rake")
115
- end
116
-
117
- @instances << [ Thread.new { job.run(free_instance_number) },
118
- free_instance_number, job ]
119
- @last_requester_mac = job.requester_mac
120
- loop do
121
- clear_completed_instances
122
- break unless max_instances_running?
123
- end
124
- end
125
- end
126
-
127
- def max_jruby_instances?
128
- return unless @config.max_jruby_instances
129
- @instances.find_all { |thread, n, job| job.jruby? }.size >= @config.max_jruby_instances
130
- end
131
-
132
- def fetch_code(job)
133
- system "rsync -az --delete -e ssh #{job.root}/ #{job.project}"
134
- end
135
-
136
- def before_run(job)
137
- bundler_cmd = RubyEnv.bundler?(job.project) ? "bundle; " : ""
138
- system "export RAILS_ENV=test; export TEST_INSTANCES=#{@config.max_instances}; cd #{job.project}; #{bundler_cmd} rake testbot:before_run"
139
- end
140
-
141
- def first_job_from_requester?
142
- @last_requester_mac == nil
143
- end
144
-
145
- def cpu_available?
146
- @instances.size > 0 || CPU.current_usage < MAX_CPU_USAGE_WHEN_IDLE
147
- end
148
-
149
- def time_for_update?
150
- time_for_update = ((Time.now - @last_version_check) >= TIME_BETWEEN_VERSION_CHECKS)
151
- @last_version_check = Time.now if time_for_update
152
- time_for_update
153
- end
154
-
155
- def check_for_update
156
- return unless @config.auto_update
157
- version = Server.get('/version') rescue Testbot.version
158
- return unless version != Testbot.version
159
-
160
- # In a PXE cluster with a shared gem folder we only want one of them to do the update
161
- if @config.wait_for_updated_gem
162
- # Gem.available? is cached so it won't detect new gems.
163
- gem = Gem::Dependency.new("testbot", version)
164
- successful_install = !Gem::SourceIndex.from_installed_gems.search(gem).empty?
165
- else
166
- if version.include?(".DEV.")
167
- successful_install = system("wget #{@config.dev_gem_root}/testbot-#{version}.gem && gem install testbot-#{version}.gem --no-ri --no-rdoc && rm testbot-#{version}.gem")
168
- else
169
- successful_install = system "gem install testbot -v #{version} --no-ri --no-rdoc"
170
- end
171
- end
172
-
173
- system "testbot #{ARGV.join(' ')}" if successful_install
174
- end
175
-
176
- def ping_params
177
- { :hostname => (@hostname ||= `hostname`.chomp), :max_instances => @config.max_instances,
178
- :idle_instances => (@config.max_instances - @instances.size), :username => ENV['USER'] }.merge(base_params)
179
- end
180
-
181
- def base_params
182
- { :version => Testbot.version, :uid => @uid }
183
- end
184
-
185
- def max_instances_running?
186
- @instances.size == @config.max_instances
187
- end
188
-
189
- def clear_completed_instances
190
- @instances.each_with_index do |data, index|
191
- @instances.delete_at(index) if data.first.join(0.25)
192
- end
193
- end
194
-
195
- def free_instance_number
196
- 0.upto(@config.max_instances - 1) do |number|
197
- return number unless @instances.find { |instance, n, job| n == number }
198
- end
199
- end
200
-
201
- def start_ping
202
- Thread.new do
203
- while true
204
- begin
205
- Server.get("/runners/ping", :body => ping_params)
206
- rescue
207
- end
208
- sleep TIME_BETWEEN_PINGS
209
- end
210
- end
211
- end
212
-
213
- end
data/lib/server.rb DELETED
@@ -1,72 +0,0 @@
1
- require 'rubygems'
2
- require 'sinatra'
3
- require 'yaml'
4
- require 'json'
5
- require File.join(File.dirname(__FILE__), 'server/job.rb')
6
- require File.join(File.dirname(__FILE__), 'server/group.rb') unless defined?(Group)
7
- require File.join(File.dirname(__FILE__), 'server/runner.rb')
8
- require File.join(File.dirname(__FILE__), 'server/build.rb')
9
- require File.expand_path(File.join(File.dirname(__FILE__), 'testbot'))
10
-
11
- if ENV['INTEGRATION_TEST']
12
- set :port, 22880
13
- else
14
- set :port, Testbot::SERVER_PORT
15
- end
16
-
17
- disable :logging if ENV['DISABLE_LOGGING']
18
-
19
- class Server
20
- def self.valid_version?(runner_version)
21
- Testbot.version == runner_version
22
- end
23
- end
24
-
25
- post '/builds' do
26
- build = Build.create_and_build_jobs(params)[:id].to_s
27
- end
28
-
29
- get '/builds/:id' do
30
- build = Build.find(:id => params[:id].to_i)
31
- build.destroy if build[:done]
32
- { "done" => build[:done], "results" => build[:results], "success" => build[:success] }.to_json
33
- end
34
-
35
- get '/jobs/next' do
36
- next_job, runner = Job.next(params, @env['REMOTE_ADDR'])
37
- if next_job
38
- next_job.update(:taken_at => Time.now, :taken_by_id => runner.id)
39
- [ next_job[:id], next_job[:requester_mac], next_job[:project], next_job[:root], next_job[:type], (next_job[:jruby] == 1 ? 'jruby' : 'ruby'), next_job[:files] ].join(',')
40
- end
41
- end
42
-
43
- put '/jobs/:id' do
44
- Job.find(:id => params[:id].to_i).update(:result => params[:result], :success => params[:success]); nil
45
- end
46
-
47
- get '/runners/ping' do
48
- return unless Server.valid_version?(params[:version])
49
- runner = Runner.find(:uid => params[:uid])
50
- runner.update(params.merge({ :last_seen_at => Time.now })) if runner
51
- nil
52
- end
53
-
54
- get '/runners/outdated' do
55
- Runner.find_all_outdated.map { |runner| [ runner[:ip], runner[:hostname], runner[:uid] ].join(' ') }.join("\n").strip
56
- end
57
-
58
- get '/runners/available_instances' do
59
- Runner.available_instances.to_s
60
- end
61
-
62
- get '/runners/total_instances' do
63
- Runner.total_instances.to_s
64
- end
65
-
66
- get '/runners/available' do
67
- Runner.find_all_available.map { |runner| [ runner[:ip], runner[:hostname], runner[:uid], runner[:username], runner[:idle_instances] ].join(' ') }.join("\n").strip
68
- end
69
-
70
- get '/version' do
71
- Testbot.version
72
- end