testbot 0.4.7 → 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README.markdown +1 -1
- data/lib/runner/job.rb +1 -1
- data/lib/server/build.rb +14 -10
- data/lib/server/job.rb +23 -24
- data/lib/server/memory_model.rb +87 -0
- data/lib/server/runner.rb +13 -8
- data/lib/server/server.rb +11 -10
- data/lib/shared/testbot.rb +1 -1
- data/testbot.gemspec +1 -5
- metadata +11 -43
- data/lib/server/db.rb +0 -49
data/CHANGELOG
CHANGED
data/README.markdown
CHANGED
@@ -69,7 +69,7 @@ Using testbot with Rails 3:
|
|
69
69
|
|
70
70
|
Using testbot with Rails 2:
|
71
71
|
|
72
|
-
ruby script/plugin install git://github.com/joakimk/testbot.git -r 'refs/tags/v0.4.
|
72
|
+
ruby script/plugin install git://github.com/joakimk/testbot.git -r 'refs/tags/v0.4.8'
|
73
73
|
script/generate testbot --connect 192.168.0.100
|
74
74
|
|
75
75
|
rake testbot:spec (or :test, :features)
|
data/lib/runner/job.rb
CHANGED
data/lib/server/build.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module Testbot::Server
|
2
2
|
|
3
|
-
class Build <
|
3
|
+
class Build < MemoryModel
|
4
|
+
|
5
|
+
def initialize(hash)
|
6
|
+
super({ :success => true, :done => false, :results => '' }.merge(hash))
|
7
|
+
end
|
4
8
|
|
5
9
|
def self.create_and_build_jobs(hash)
|
6
10
|
hash["jruby"] = (hash["jruby"] == "true") ? 1 : 0
|
@@ -10,21 +14,21 @@ module Testbot::Server
|
|
10
14
|
end
|
11
15
|
|
12
16
|
def create_jobs!(available_runner_usage)
|
13
|
-
groups = Group.build(self
|
14
|
-
Runner.total_instances.to_f * (available_runner_usage.to_i / 100.0), self
|
17
|
+
groups = Group.build(self.files.split, self.sizes.split.map { |size| size.to_i },
|
18
|
+
Runner.total_instances.to_f * (available_runner_usage.to_i / 100.0), self.type)
|
15
19
|
groups.each do |group|
|
16
20
|
Job.create(:files => group.join(' '),
|
17
|
-
:root => self
|
18
|
-
:project => self
|
19
|
-
:type => self
|
20
|
-
:requester_mac => self
|
21
|
-
:
|
22
|
-
:jruby => self
|
21
|
+
:root => self.root,
|
22
|
+
:project => self.project,
|
23
|
+
:type => self.type,
|
24
|
+
:requester_mac => self.requester_mac,
|
25
|
+
:build => self,
|
26
|
+
:jruby => self.jruby)
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
30
|
def destroy
|
27
|
-
Job.
|
31
|
+
Job.all.find_all { |j| j.build == self }.each { |job| job.destroy }
|
28
32
|
super
|
29
33
|
end
|
30
34
|
|
data/lib/server/job.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'db.rb'))
|
2
|
-
|
3
1
|
module Testbot::Server
|
4
2
|
|
5
|
-
class Job <
|
3
|
+
class Job < MemoryModel
|
4
|
+
|
5
|
+
attribute :success, :boolean
|
6
|
+
|
6
7
|
def update(hash)
|
7
8
|
super(hash)
|
8
|
-
if build
|
9
|
-
done = Job.
|
10
|
-
build.update(:results => build
|
11
|
-
|
9
|
+
if self.build
|
10
|
+
done = !Job.all.find { |j| !j.result && j.build == self.build }
|
11
|
+
self.build.update(:results => build.results.to_s + self.result.to_s,
|
12
|
+
:done => done)
|
12
13
|
|
13
|
-
build_broken_by_job = (
|
14
|
-
build.update(:success => false) if build_broken_by_job
|
14
|
+
build_broken_by_job = (!self.success && build.success)
|
15
|
+
self.build.update(:success => false) if build_broken_by_job
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
@@ -19,29 +20,27 @@ module Testbot::Server
|
|
19
20
|
clean_params = params.reject { |k, v| [ "requester_mac", "no_jruby" ].include?(k) }
|
20
21
|
runner = Runner.record! clean_params.merge({ :ip => remove_addr, :last_seen_at => Time.now })
|
21
22
|
return unless Server.valid_version?(params[:version])
|
22
|
-
[
|
23
|
+
[ next_job(params["requester_mac"], params["no_jruby"]), runner ]
|
23
24
|
end
|
24
25
|
|
25
26
|
private
|
26
27
|
|
27
|
-
def self.
|
28
|
+
def self.next_job(requester_mac, no_jruby)
|
28
29
|
release_jobs_taken_by_missing_runners!
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
query.filter(filters.join(' AND '))
|
37
|
-
end
|
30
|
+
jobs = Job.all.find_all { |j|
|
31
|
+
!j.taken_at &&
|
32
|
+
(requester_mac ? j.requester_mac == requester_mac : true) &&
|
33
|
+
(no_jruby ? j.jruby != 1 : true)
|
34
|
+
}
|
35
|
+
|
36
|
+
jobs[rand(jobs.size)]
|
38
37
|
end
|
39
38
|
|
40
39
|
def self.release_jobs_taken_by_missing_runners!
|
41
|
-
missing_runners = Runner.
|
42
|
-
missing_runners.each { |
|
43
|
-
Job.
|
44
|
-
}
|
40
|
+
missing_runners = Runner.all.find_all { |r| r.last_seen_at < (Time.now - Runner.timeout) }
|
41
|
+
missing_runners.each { |runner|
|
42
|
+
Job.all.find_all { |job| job.taken_by == runner }.each { |job| job.update(:taken_at => nil) }
|
43
|
+
}
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class MemoryModel < OpenStruct
|
2
|
+
|
3
|
+
@@db = {}
|
4
|
+
@@types = {}
|
5
|
+
|
6
|
+
def initialize(hash)
|
7
|
+
@@types[self.class] ||= {}
|
8
|
+
hash = resolve_types(symbolize_keys(hash))
|
9
|
+
super(hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
def id
|
13
|
+
object_id
|
14
|
+
end
|
15
|
+
|
16
|
+
def type
|
17
|
+
@table[:type]
|
18
|
+
end
|
19
|
+
|
20
|
+
def update(hash)
|
21
|
+
@table.merge!(resolve_types(symbolize_keys(hash)))
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy
|
26
|
+
self.class.all.delete_if { |b| b.id == id }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.find(id)
|
30
|
+
all.find { |r| r.id == id.to_i }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.create(hash = {})
|
34
|
+
all << new(hash)
|
35
|
+
all[-1]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.all
|
39
|
+
@@db[self] ||= []
|
40
|
+
@@db[self]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.first
|
44
|
+
all.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.delete_all
|
48
|
+
all.clear
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.count
|
52
|
+
all.size
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.attribute(attribute, type)
|
56
|
+
@@types[self] ||= {}
|
57
|
+
@@types[self][attribute] = type
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def resolve_types(hash)
|
63
|
+
hash.each { |attribute, value|
|
64
|
+
case @@types[self.class][attribute]
|
65
|
+
when :integer
|
66
|
+
hash[attribute] = value.to_i
|
67
|
+
when :boolean
|
68
|
+
if value == "true"
|
69
|
+
hash[attribute] = true
|
70
|
+
elsif value == "false"
|
71
|
+
hash[attribute] = false
|
72
|
+
elsif value != true && value != false
|
73
|
+
hash[attribute] = nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
}
|
77
|
+
hash
|
78
|
+
end
|
79
|
+
|
80
|
+
def symbolize_keys(hash)
|
81
|
+
h = {}
|
82
|
+
hash.each { |k, v| h[k.to_sym] = v }
|
83
|
+
h
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
data/lib/server/runner.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'db.rb'))
|
2
|
-
|
3
1
|
module Testbot::Server
|
4
2
|
|
5
|
-
class Runner <
|
3
|
+
class Runner < MemoryModel
|
4
|
+
|
5
|
+
attribute :idle_instances, :integer
|
6
|
+
attribute :max_instances, :integer
|
6
7
|
|
7
8
|
def self.record!(hash)
|
8
9
|
create_or_update_by_mac!(hash)
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.create_or_update_by_mac!(hash)
|
12
|
-
if
|
13
|
+
if runner = find_by_uid(hash[:uid])
|
13
14
|
runner.update hash
|
14
15
|
else
|
15
16
|
Runner.create hash
|
@@ -20,21 +21,25 @@ module Testbot::Server
|
|
20
21
|
10
|
21
22
|
end
|
22
23
|
|
24
|
+
def self.find_by_uid(uid)
|
25
|
+
all.find { |r| r.uid == uid }
|
26
|
+
end
|
27
|
+
|
23
28
|
def self.find_all_outdated
|
24
|
-
|
29
|
+
all.find_all { |r| r.version != Testbot.version }
|
25
30
|
end
|
26
31
|
|
27
32
|
def self.find_all_available
|
28
|
-
|
33
|
+
all.find_all { |r| r.version == Testbot.version && r.last_seen_at > (Time.now - Runner.timeout) }
|
29
34
|
end
|
30
35
|
|
31
36
|
def self.available_instances
|
32
|
-
find_all_available.inject(0) { |sum, r| r
|
37
|
+
find_all_available.inject(0) { |sum, r| r.idle_instances + sum }
|
33
38
|
end
|
34
39
|
|
35
40
|
def self.total_instances
|
36
41
|
return 1 if ENV['INTEGRATION_TEST']
|
37
|
-
find_all_available.inject(0) { |sum, r| r
|
42
|
+
find_all_available.inject(0) { |sum, r| r.max_instances + sum }
|
38
43
|
end
|
39
44
|
|
40
45
|
end
|
data/lib/server/server.rb
CHANGED
@@ -3,6 +3,7 @@ require 'sinatra'
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'json'
|
5
5
|
require File.expand_path(File.join(File.dirname(__FILE__), '/../shared/testbot'))
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'memory_model.rb'))
|
6
7
|
require File.expand_path(File.join(File.dirname(__FILE__), 'job.rb'))
|
7
8
|
require File.expand_path(File.join(File.dirname(__FILE__), 'group.rb')) #unless defined?(Group)
|
8
9
|
require File.expand_path(File.join(File.dirname(__FILE__), 'runner.rb'))
|
@@ -23,36 +24,36 @@ module Testbot::Server
|
|
23
24
|
end
|
24
25
|
|
25
26
|
post '/builds' do
|
26
|
-
build = Build.create_and_build_jobs(params)
|
27
|
+
build = Build.create_and_build_jobs(params).id.to_s
|
27
28
|
end
|
28
29
|
|
29
30
|
get '/builds/:id' do
|
30
|
-
build = Build.find(
|
31
|
-
build.destroy if build
|
32
|
-
{ "done" => build
|
31
|
+
build = Build.find(params[:id])
|
32
|
+
build.destroy if build.done
|
33
|
+
{ "done" => build.done, "results" => build.results, "success" => build.success }.to_json
|
33
34
|
end
|
34
35
|
|
35
36
|
get '/jobs/next' do
|
36
37
|
next_job, runner = Job.next(params, @env['REMOTE_ADDR'])
|
37
38
|
if next_job
|
38
|
-
next_job.update(:taken_at => Time.now, :
|
39
|
-
[ next_job
|
39
|
+
next_job.update(:taken_at => Time.now, :taken_by => runner)
|
40
|
+
[ 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
41
|
end
|
41
42
|
end
|
42
43
|
|
43
44
|
put '/jobs/:id' do
|
44
|
-
Job.find(
|
45
|
+
Job.find(params[:id]).update(:result => params[:result], :success => params[:success]); nil
|
45
46
|
end
|
46
47
|
|
47
48
|
get '/runners/ping' do
|
48
49
|
return unless Server.valid_version?(params[:version])
|
49
|
-
runner = Runner.
|
50
|
+
runner = Runner.find_by_uid(params[:uid])
|
50
51
|
runner.update(params.merge({ :last_seen_at => Time.now })) if runner
|
51
52
|
nil
|
52
53
|
end
|
53
54
|
|
54
55
|
get '/runners/outdated' do
|
55
|
-
Runner.find_all_outdated.map { |runner| [ runner
|
56
|
+
Runner.find_all_outdated.map { |runner| [ runner.ip, runner.hostname, runner.uid ].join(' ') }.join("\n").strip
|
56
57
|
end
|
57
58
|
|
58
59
|
get '/runners/available_instances' do
|
@@ -64,7 +65,7 @@ module Testbot::Server
|
|
64
65
|
end
|
65
66
|
|
66
67
|
get '/runners/available' do
|
67
|
-
Runner.find_all_available.map { |runner| [ runner
|
68
|
+
Runner.find_all_available.map { |runner| [ runner.ip, runner.hostname, runner.uid, runner.username, runner.idle_instances ].join(' ') }.join("\n").strip
|
68
69
|
end
|
69
70
|
|
70
71
|
get '/version' do
|
data/lib/shared/testbot.rb
CHANGED
@@ -7,7 +7,7 @@ module Testbot
|
|
7
7
|
|
8
8
|
# Don't forget to update readme and changelog
|
9
9
|
def self.version
|
10
|
-
version = "0.4.
|
10
|
+
version = "0.4.8"
|
11
11
|
dev_version_file = File.join(File.dirname(__FILE__), '..', '..', 'DEV_VERSION')
|
12
12
|
if File.exists?(dev_version_file)
|
13
13
|
version += File.read(dev_version_file)
|
data/testbot.gemspec
CHANGED
@@ -17,12 +17,8 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_dependency('httparty', '>= 0.6.1')
|
18
18
|
s.add_dependency('macaddr', '>= 1.0.0')
|
19
19
|
s.add_dependency('net-ssh', '>= 2.0.23')
|
20
|
-
s.add_dependency('
|
21
|
-
s.add_dependency('json', '>= 1.4.6')
|
20
|
+
s.add_dependency('json_pure', '>= 1.4.6')
|
22
21
|
s.add_dependency('daemons', '>= 1.0.10')
|
23
22
|
s.add_dependency('acts_as_rails3_generator')
|
24
|
-
|
25
|
-
# Could work with older versions, but not newer (when deploying on debian)
|
26
|
-
s.add_dependency('sqlite3-ruby', '= 1.2.5')
|
27
23
|
end
|
28
24
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 8
|
10
|
+
version: 0.4.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Joakim Kolsj\xC3\xB6"
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-12-
|
18
|
+
date: 2010-12-06 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -83,25 +83,9 @@ dependencies:
|
|
83
83
|
type: :runtime
|
84
84
|
version_requirements: *id004
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
|
-
name:
|
86
|
+
name: json_pure
|
87
87
|
prerelease: false
|
88
88
|
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ">="
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
hash: 71
|
94
|
-
segments:
|
95
|
-
- 3
|
96
|
-
- 16
|
97
|
-
- 0
|
98
|
-
version: 3.16.0
|
99
|
-
type: :runtime
|
100
|
-
version_requirements: *id005
|
101
|
-
- !ruby/object:Gem::Dependency
|
102
|
-
name: json
|
103
|
-
prerelease: false
|
104
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
105
89
|
none: false
|
106
90
|
requirements:
|
107
91
|
- - ">="
|
@@ -113,11 +97,11 @@ dependencies:
|
|
113
97
|
- 6
|
114
98
|
version: 1.4.6
|
115
99
|
type: :runtime
|
116
|
-
version_requirements: *
|
100
|
+
version_requirements: *id005
|
117
101
|
- !ruby/object:Gem::Dependency
|
118
102
|
name: daemons
|
119
103
|
prerelease: false
|
120
|
-
requirement: &
|
104
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
121
105
|
none: false
|
122
106
|
requirements:
|
123
107
|
- - ">="
|
@@ -129,11 +113,11 @@ dependencies:
|
|
129
113
|
- 10
|
130
114
|
version: 1.0.10
|
131
115
|
type: :runtime
|
132
|
-
version_requirements: *
|
116
|
+
version_requirements: *id006
|
133
117
|
- !ruby/object:Gem::Dependency
|
134
118
|
name: acts_as_rails3_generator
|
135
119
|
prerelease: false
|
136
|
-
requirement: &
|
120
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
137
121
|
none: false
|
138
122
|
requirements:
|
139
123
|
- - ">="
|
@@ -143,23 +127,7 @@ dependencies:
|
|
143
127
|
- 0
|
144
128
|
version: "0"
|
145
129
|
type: :runtime
|
146
|
-
version_requirements: *
|
147
|
-
- !ruby/object:Gem::Dependency
|
148
|
-
name: sqlite3-ruby
|
149
|
-
prerelease: false
|
150
|
-
requirement: &id009 !ruby/object:Gem::Requirement
|
151
|
-
none: false
|
152
|
-
requirements:
|
153
|
-
- - "="
|
154
|
-
- !ruby/object:Gem::Version
|
155
|
-
hash: 21
|
156
|
-
segments:
|
157
|
-
- 1
|
158
|
-
- 2
|
159
|
-
- 5
|
160
|
-
version: 1.2.5
|
161
|
-
type: :runtime
|
162
|
-
version_requirements: *id009
|
130
|
+
version_requirements: *id007
|
163
131
|
description: Testbot is a test distribution tool that works with Rails, RSpec, Test::Unit and Cucumber.
|
164
132
|
email:
|
165
133
|
- joakim.kolsjo@gmail.com
|
@@ -178,9 +146,9 @@ files:
|
|
178
146
|
- lib/runner/job.rb
|
179
147
|
- lib/runner/runner.rb
|
180
148
|
- lib/server/build.rb
|
181
|
-
- lib/server/db.rb
|
182
149
|
- lib/server/group.rb
|
183
150
|
- lib/server/job.rb
|
151
|
+
- lib/server/memory_model.rb
|
184
152
|
- lib/server/runner.rb
|
185
153
|
- lib/server/server.rb
|
186
154
|
- lib/shared/adapters/adapter.rb
|
data/lib/server/db.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'sequel'
|
2
|
-
|
3
|
-
module Testbot::Server
|
4
|
-
|
5
|
-
DB = Sequel.sqlite
|
6
|
-
|
7
|
-
DB.create_table :builds do
|
8
|
-
primary_key :id
|
9
|
-
String :files
|
10
|
-
String :sizes
|
11
|
-
String :results, :default => ''
|
12
|
-
String :root
|
13
|
-
String :project
|
14
|
-
String :type
|
15
|
-
String :requester_mac
|
16
|
-
Integer :jruby
|
17
|
-
Boolean :success, :default => true
|
18
|
-
Boolean :done, :default => false
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
DB.create_table :jobs do
|
23
|
-
primary_key :id
|
24
|
-
String :files
|
25
|
-
String :result
|
26
|
-
String :root
|
27
|
-
String :project
|
28
|
-
String :type
|
29
|
-
String :requester_mac
|
30
|
-
Integer :jruby
|
31
|
-
Integer :build_id
|
32
|
-
Integer :taken_by_id
|
33
|
-
Boolean :success
|
34
|
-
Datetime :taken_at, :default => nil
|
35
|
-
end
|
36
|
-
|
37
|
-
DB.create_table :runners do
|
38
|
-
primary_key :id
|
39
|
-
String :ip
|
40
|
-
String :hostname
|
41
|
-
String :uid
|
42
|
-
String :username
|
43
|
-
String :version
|
44
|
-
Integer :idle_instances
|
45
|
-
Integer :max_instances
|
46
|
-
Datetime :last_seen_at
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|