testbot 0.4.6 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
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