testbot_cloud 0.1.0
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/.autotest +7 -0
- data/.gemtest +0 -0
- data/.gitignore +9 -0
- data/.rvmrc +1 -0
- data/CHANGELOG +0 -0
- data/Gemfile +4 -0
- data/README.markdown +86 -0
- data/Rakefile +16 -0
- data/autotest/discover.rb +1 -0
- data/bin/testbot_cloud +10 -0
- data/features/cluster_management.feature +13 -0
- data/features/project_creation.feature +14 -0
- data/features/step_definitions/shared_steps.rb +35 -0
- data/lib/cli.rb +38 -0
- data/lib/cluster.rb +111 -0
- data/lib/server/aws.rb +19 -0
- data/lib/server/brightbox.rb +38 -0
- data/lib/server/factory.rb +21 -0
- data/lib/server/shared/bootstrap.rb +60 -0
- data/lib/servers.rb +15 -0
- data/lib/templates/config.yml +53 -0
- data/lib/templates/gitignore +1 -0
- data/lib/templates/runner.sh +25 -0
- data/lib/testbot_cloud.rb +5 -0
- data/lib/testbot_cloud/version.rb +3 -0
- data/spec/cli_spec.rb +56 -0
- data/spec/cluster_spec.rb +223 -0
- data/spec/server/aws_spec.rb +18 -0
- data/spec/server/brightbox_spec.rb +39 -0
- data/spec/server/factory_spec.rb +22 -0
- data/spec/server/shared/bootstrap_spec.rb +39 -0
- data/spec/spec_helper.rb +1 -0
- data/testbot_cloud.gemspec +26 -0
- metadata +189 -0
data/.autotest
ADDED
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ree-1.8.7-2011.03@testbot_cloud --create
|
data/CHANGELOG
ADDED
File without changes
|
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
A tool for creating and managing [testbot](https://github.com/joakimk/testbot) clusters in the cloud.
|
2
|
+
|
3
|
+
TestbotCloud is based around the idea that you have a project folder for each cluster (which you can store in verison control). You then use the "testbot_cloud" command to start and stop the cluster.
|
4
|
+
|
5
|
+
The motivation behind this tool, besides making distributed testing simpler is to be able to run a cluster only when it's needed (by scheduling it with tools like cron).
|
6
|
+
|
7
|
+
Installing
|
8
|
+
----
|
9
|
+
|
10
|
+
gem install testbot_cloud
|
11
|
+
|
12
|
+
Getting started
|
13
|
+
----
|
14
|
+
|
15
|
+
Using AWS EC2:
|
16
|
+
|
17
|
+
* Get a AWS account at [http://aws.amazon.com/](http://aws.amazon.com/).
|
18
|
+
* Create a Key Pair.
|
19
|
+
* Allow SSH login to a security group. For example: SSH, tcp, 22, 22, 0.0.0.0/0.
|
20
|
+
|
21
|
+
Using Brightbox:
|
22
|
+
|
23
|
+
* Get a beta account at [http://beta.brightbox.com/beta](http://beta.brightbox.com/beta).
|
24
|
+
* Follow [http://docs.brightbox.com/guides/getting_started](http://docs.brightbox.com/guides/getting_started) to setup a SSH key.
|
25
|
+
|
26
|
+
Creating a cluster
|
27
|
+
----
|
28
|
+
|
29
|
+
Create a project
|
30
|
+
|
31
|
+
testbot_cloud new demo
|
32
|
+
|
33
|
+
# create demo/config.yml
|
34
|
+
# create demo/.gitignore
|
35
|
+
# create demo/bootstrap/runner.sh
|
36
|
+
|
37
|
+
Start
|
38
|
+
|
39
|
+
cd demo
|
40
|
+
testbot_cloud start
|
41
|
+
|
42
|
+
# Starting 1 runners...
|
43
|
+
# i-dd2222dd is being created...
|
44
|
+
# i-dd2222dd is up, installing testbot...
|
45
|
+
# i-dd2222dd ready.
|
46
|
+
|
47
|
+
Shutdown
|
48
|
+
|
49
|
+
testbot_cloud stop
|
50
|
+
|
51
|
+
# Shutting down i-dd2222dd...
|
52
|
+
|
53
|
+
Debugging
|
54
|
+
----
|
55
|
+
|
56
|
+
* Run **DEBUG=true testbot_cloud start** to show all SSH commands and output.
|
57
|
+
|
58
|
+
Gotchas
|
59
|
+
-----
|
60
|
+
|
61
|
+
* Don't create more than 10-15 or so runners at a time (some cloud providers don't allow more than that many connections at once). This might be fixed by batching the creation process in a later version of TestbotCloud.
|
62
|
+
|
63
|
+
* Don't create more than 20 runners in total on EC2 as it has a limit by default. See the [EC2 FAQ](http://aws.amazon.com/ec2/faqs) for more info.
|
64
|
+
|
65
|
+
Features
|
66
|
+
-----
|
67
|
+
|
68
|
+
* TestbotCloud is continuously tested for compability with Ruby 1.8.7, 1.9.2, JRuby 1.5.5 and Rubinius 1.1.1.
|
69
|
+
* TestbotCloud is designed to be as reliable as possible when starting and stopping so that you can schedule it with tools like cron and save money.
|
70
|
+
|
71
|
+
How to add support for additional cloud computing providers
|
72
|
+
-----
|
73
|
+
|
74
|
+
Basics:
|
75
|
+
|
76
|
+
* Look at lib/server/aws.rb and lib/server/brightbox.rb.
|
77
|
+
* Write you own and add it to lib/server/factory.rb.
|
78
|
+
* Add fog config suitable for the provider to your config.yml.
|
79
|
+
|
80
|
+
When contributing:
|
81
|
+
|
82
|
+
* Make sure you have the tests running on your machine (should be just running "bundle" and "rake").
|
83
|
+
* Write tests.
|
84
|
+
* Add a config example to the template at lib/templates/config.yml.
|
85
|
+
* Update this readme.
|
86
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'cucumber'
|
3
|
+
require 'cucumber/rake/task'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
Bundler::GemHelper.install_tasks
|
7
|
+
|
8
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
9
|
+
t.cucumber_opts = "features --format progress"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Run specs"
|
13
|
+
RSpec::Core::RakeTask.new do |t|
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => [ :spec, :features ]
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/bin/testbot_cloud
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Feature: Cluster management
|
2
|
+
In order to run my tests in the cloud
|
3
|
+
As a build monkey
|
4
|
+
I want to be able to start, stop and check status on the cloud instances
|
5
|
+
|
6
|
+
Scenario: Staring with 2 instances
|
7
|
+
Given I generate a project
|
8
|
+
And I start the testbot cluster
|
9
|
+
Then I should see "Starting 2 runners..."
|
10
|
+
And I should see " up, installing testbot..."
|
11
|
+
And I should see " ready."
|
12
|
+
And I should not see any errors
|
13
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: Project creation
|
2
|
+
In order to run my tests in the cloud
|
3
|
+
As a build monkey
|
4
|
+
I want to create a project that represents my testbot cluster
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given there is no project
|
8
|
+
|
9
|
+
Scenario: Creating a project
|
10
|
+
When I generate a project
|
11
|
+
Then there should be a project folder
|
12
|
+
And the project folder should contain a config file
|
13
|
+
And the project folder should contain bootstrap files
|
14
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Given /^there is no project$/ do
|
2
|
+
system "rm -rf tmp/cluster"
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I generate a project$/ do
|
6
|
+
system("INTEGRATION_TEST=true bin/testbot_cloud new tmp/cluster 1> /dev/null") || raise
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^there should be a project folder$/ do
|
10
|
+
File.exists?("tmp/cluster") || raise
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^the project folder should contain a config file$/ do
|
14
|
+
File.exists?("tmp/cluster/config.yml") || raise
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^the project folder should contain bootstrap files$/ do
|
18
|
+
File.exists?("tmp/cluster/bootstrap/runner.sh") || raise
|
19
|
+
end
|
20
|
+
|
21
|
+
Given /^I start the testbot cluster$/ do
|
22
|
+
@results = `cd tmp/cluster; INTEGRATION_TEST=true ../../bin/testbot_cloud start 2>&1`
|
23
|
+
end
|
24
|
+
|
25
|
+
Then /^I should see "([^"]*)"$/ do |text|
|
26
|
+
@results.include?(text) || raise("Did not see '#{text}' in #{@results}")
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^I should not see any errors$/ do
|
30
|
+
if @results.include?("testbot_cloud/lib")
|
31
|
+
puts @results
|
32
|
+
raise
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'thor'
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'cluster.rb'))
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'testbot_cloud/version.rb'))
|
5
|
+
|
6
|
+
module TestbotCloud
|
7
|
+
class Cli < Thor
|
8
|
+
include Thor::Actions
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
File.dirname(__FILE__) + "/templates"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "new PROJECT_NAME", "Generate a testbot cloud project"
|
15
|
+
def new(name)
|
16
|
+
copy_file "config.yml", "#{name}/config.yml"
|
17
|
+
copy_file "gitignore", "#{name}/.gitignore"
|
18
|
+
copy_file "runner.sh", "#{name}/bootstrap/runner.sh"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "start", "Start a testbot cluster as configured in config.yml"
|
22
|
+
def start
|
23
|
+
Cluster.new.start
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "stop", "Shutdown servers"
|
27
|
+
def stop
|
28
|
+
Cluster.new.stop
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "version", "Show version"
|
32
|
+
def version
|
33
|
+
puts "TestbotCloud #{TestbotCloud::VERSION}"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
data/lib/cluster.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'fog'
|
2
|
+
require 'yaml'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'server/factory.rb'))
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'servers.rb'))
|
6
|
+
|
7
|
+
module TestbotCloud
|
8
|
+
class Cluster
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
Fog.mock! if ENV['INTEGRATION_TEST']
|
12
|
+
if project?
|
13
|
+
load_config
|
14
|
+
|
15
|
+
@compute = Fog::Compute.new(@provider_config)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
project? || return
|
21
|
+
|
22
|
+
puts "Starting #{@runner_count} runners..."
|
23
|
+
for_each_runner_in_a_thread do |mutex|
|
24
|
+
server = nil
|
25
|
+
with_retries("server creation") do
|
26
|
+
mutex.synchronize {
|
27
|
+
server = @compute.servers.create(@runner_config)
|
28
|
+
Servers.log_creation(server)
|
29
|
+
|
30
|
+
# Brightbox API is a bit unstable when creating multiple servers at the same time
|
31
|
+
if @provider_config[:provider] == "Brightbox"
|
32
|
+
# < Caius> joakimk2: yeah, there's a race condition with creating two servers within the
|
33
|
+
# same second currently. We're working on a fix. Workaround is to
|
34
|
+
# wait a second or two before creating the second.
|
35
|
+
sleep 3
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "#{server.id} is being created..."
|
41
|
+
with_retries("#{server.id} ready check") do
|
42
|
+
server.wait_for { ready? }
|
43
|
+
end
|
44
|
+
|
45
|
+
puts "#{server.id} is up, installing testbot..."
|
46
|
+
with_retries("testbot installation") do
|
47
|
+
if Server::Factory.create(@compute, @opts, server).bootstrap!(mutex)
|
48
|
+
puts "#{server.id} ready."
|
49
|
+
else
|
50
|
+
puts "#{server.id} failed, shutting down."
|
51
|
+
server.destroy
|
52
|
+
Servers.log_destruction(server)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def stop
|
59
|
+
project? || return
|
60
|
+
|
61
|
+
@compute.servers.each do |server|
|
62
|
+
if Servers.known?(server) && server.ready?
|
63
|
+
puts "Shutting down #{server.id}..."
|
64
|
+
with_retries("server destruction") do
|
65
|
+
server.destroy
|
66
|
+
end
|
67
|
+
Servers.log_destruction(server)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def project?
|
75
|
+
File.exists?("config.yml")
|
76
|
+
end
|
77
|
+
|
78
|
+
def for_each_runner_in_a_thread
|
79
|
+
threads = []
|
80
|
+
mutex = Mutex.new
|
81
|
+
@runner_count.times do
|
82
|
+
threads << Thread.new do
|
83
|
+
yield mutex
|
84
|
+
end
|
85
|
+
end
|
86
|
+
threads.each { |thread| thread.join }
|
87
|
+
end
|
88
|
+
|
89
|
+
def load_config
|
90
|
+
config = YAML.load_file("config.yml")
|
91
|
+
@provider_config = config["provider"].symbolize_keys
|
92
|
+
@runner_config = config["runner"].symbolize_keys
|
93
|
+
@runner_count = config["runners"]
|
94
|
+
@opts = { :ssh_user => config["ssh_user"] }
|
95
|
+
end
|
96
|
+
|
97
|
+
def with_retries(job)
|
98
|
+
5.times do
|
99
|
+
begin
|
100
|
+
yield
|
101
|
+
rescue Excon::Errors::SocketError => ex
|
102
|
+
puts "#{job} failed, retrying..."
|
103
|
+
sleep 3
|
104
|
+
else
|
105
|
+
break
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
data/lib/server/aws.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'shared', 'bootstrap.rb'))
|
2
|
+
|
3
|
+
module TestbotCloud
|
4
|
+
module Server
|
5
|
+
class AWS
|
6
|
+
def initialize(compute, opts, server)
|
7
|
+
@server, @opts = server, opts
|
8
|
+
end
|
9
|
+
|
10
|
+
def bootstrap!(mutex)
|
11
|
+
# We use the AWS adapter in integration tests.
|
12
|
+
return true if ENV['INTEGRATION_TEST']
|
13
|
+
|
14
|
+
Bootstrap.new(@server.dns_name, @server.id, @opts.merge({ :ssh_opts => "-i testbot.pem" })).install
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module TestbotCloud
|
2
|
+
module Server
|
3
|
+
class Brightbox
|
4
|
+
def initialize(compute, opts, server)
|
5
|
+
@compute, @opts, @server = compute, opts, server
|
6
|
+
end
|
7
|
+
|
8
|
+
def bootstrap!(mutex)
|
9
|
+
ip = nil
|
10
|
+
mutex.synchronize { ip = map_ip! }
|
11
|
+
Bootstrap.new(ip.public_ip, @server.id, @opts).install
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def map_ip!
|
17
|
+
ip = get_ip
|
18
|
+
ip.map(@server.interfaces.first["id"])
|
19
|
+
ip
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_ip
|
23
|
+
if available_ip = find_available_ip
|
24
|
+
available_ip
|
25
|
+
else
|
26
|
+
@compute.create_cloud_ip
|
27
|
+
find_available_ip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_available_ip
|
32
|
+
@compute.cloud_ips.find { |ip| ip.status == "unmapped" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'fog/compute/models/slicehost/server'
|
2
|
+
require 'fog/compute/models/brightbox/server'
|
3
|
+
require 'fog/compute/models/aws/server'
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'brightbox.rb'))
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'aws.rb'))
|
6
|
+
|
7
|
+
module TestbotCloud
|
8
|
+
module Server
|
9
|
+
class Factory
|
10
|
+
def self.create(compute, opts, server)
|
11
|
+
if server.is_a?(Fog::Brightbox::Compute::Server)
|
12
|
+
Brightbox.new(compute, opts, server)
|
13
|
+
elsif server.is_a?(Fog::AWS::Compute::Server)
|
14
|
+
AWS.new(compute, opts, server)
|
15
|
+
else
|
16
|
+
raise "Unsupported server type: #{server}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module TestbotCloud
|
2
|
+
module Server
|
3
|
+
class Bootstrap
|
4
|
+
def initialize(host, id, opts)
|
5
|
+
@host, @id, @opts = host, id, opts
|
6
|
+
end
|
7
|
+
|
8
|
+
def install
|
9
|
+
wait_for_server && upload_bootstrap_files && run('cd bootstrap; sudo sh runner.sh')
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def wait_for_server
|
15
|
+
fail_count = 0
|
16
|
+
10.times do
|
17
|
+
if run('true')
|
18
|
+
return true
|
19
|
+
else
|
20
|
+
fail_count += 1
|
21
|
+
|
22
|
+
# Usally fails atleast once, so better to keep it quiet to begin with
|
23
|
+
if fail_count > 2
|
24
|
+
puts "#{@id} ssh connection failed, retrying..."
|
25
|
+
end
|
26
|
+
|
27
|
+
sleep 3
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def upload_bootstrap_files
|
35
|
+
run_with_debug("scp #{ssh_opts("-r bootstrap")}:~ &> /dev/null")
|
36
|
+
end
|
37
|
+
|
38
|
+
def run(command)
|
39
|
+
run_with_debug("ssh #{ssh_opts} '#{command}' &> /dev/null")
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_with_debug(cmd)
|
43
|
+
if ENV["DEBUG"]
|
44
|
+
puts "DEBUG - CMD: #{cmd}"
|
45
|
+
return_status = system(cmd.gsub(/&.+/, ''))
|
46
|
+
puts "DEBUG - SUCCESS: #{return_status}"
|
47
|
+
return_status
|
48
|
+
else
|
49
|
+
system(cmd)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def ssh_opts(custom_opts = nil)
|
54
|
+
[ "-o StrictHostKeyChecking=no", @opts[:ssh_opts],
|
55
|
+
custom_opts, "#{@opts[:ssh_user]}@#{@host}" ].compact.join(' ')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/lib/servers.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module TestbotCloud
|
2
|
+
class Servers
|
3
|
+
def self.log_creation(server)
|
4
|
+
FileUtils.mkdir_p ".servers/#{server.id}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.known?(server)
|
8
|
+
File.exists? ".servers/#{server.id}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.log_destruction(server)
|
12
|
+
FileUtils.rm_rf ".servers/#{server.id}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# How many instances to run
|
2
|
+
runners: 2
|
3
|
+
|
4
|
+
# The name of the user provided by the cloud image
|
5
|
+
ssh_user: ubuntu
|
6
|
+
|
7
|
+
# Using EC2
|
8
|
+
provider:
|
9
|
+
provider: AWS
|
10
|
+
aws_access_key_id: AWS_ACCESS_ID
|
11
|
+
aws_secret_access_key: AWS_ACCESS_KEY
|
12
|
+
|
13
|
+
runner:
|
14
|
+
key_name: ec2_key
|
15
|
+
groups: default
|
16
|
+
flavor_id: c1.medium
|
17
|
+
|
18
|
+
### Ubuntu 10.04 images ###
|
19
|
+
|
20
|
+
# 32 bit, ebs (use with t1.micro)
|
21
|
+
#image_id: ami-480df921
|
22
|
+
|
23
|
+
# 32 bit, instance
|
24
|
+
image_id: ami-480df921
|
25
|
+
|
26
|
+
# 64 bit, instance
|
27
|
+
#image_id: ami-da0cf8b3
|
28
|
+
|
29
|
+
# Has the cheapest instances
|
30
|
+
region: us-east-1
|
31
|
+
|
32
|
+
# Using Brightbox
|
33
|
+
#provider:
|
34
|
+
# provider: Brightbox
|
35
|
+
# brightbox_client_id: cli-XXXXX
|
36
|
+
# brightbox_secret: xxx
|
37
|
+
#
|
38
|
+
#runner:
|
39
|
+
# # Ubuntu 10.04, 32 bit
|
40
|
+
# image_id: img-hm6oj
|
41
|
+
#
|
42
|
+
# # 4 cores, 1 GB (256 MB / core)
|
43
|
+
# flavor_id: typ-iqisj
|
44
|
+
#
|
45
|
+
# # 2 cores, 512 MB (256 MB / core)
|
46
|
+
# # flavor_id: typ-4nssg
|
47
|
+
#
|
48
|
+
# # 4 cores, 2 GB (512 MB / core)
|
49
|
+
# # flavor_id: typ-urtky
|
50
|
+
#
|
51
|
+
# # 8 cores, 4 GB (512 MB / core)
|
52
|
+
# # flavor_id: typ-qdiwq
|
53
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
.servers
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
export DEBIAN_FRONTEND=noninteractive
|
3
|
+
|
4
|
+
apt-get update &&
|
5
|
+
apt-get -y install curl wget ruby ruby-dev libopenssl-ruby &&
|
6
|
+
wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz &&
|
7
|
+
tar xvfz rubygems-1.3.7.tgz && cd rubygems-1.3.7 && ruby setup.rb && ln -s /usr/bin/gem1.8 /usr/bin/gem
|
8
|
+
|
9
|
+
su ubuntu -c 'GEM_HOME="~/.gem" gem install testbot --no-ri --no-rdoc'
|
10
|
+
|
11
|
+
# Ramdisk for fast rsync
|
12
|
+
echo 'none /tmp tmpfs defaults 0 0' >> /etc/fstab
|
13
|
+
mount -a
|
14
|
+
|
15
|
+
cd /home/ubuntu
|
16
|
+
mv bootstrap/ssh/* .ssh/
|
17
|
+
chown -R ubuntu .ssh
|
18
|
+
chmod 0600 .ssh/id_rsa
|
19
|
+
|
20
|
+
# TODO:
|
21
|
+
# Haven't gotten auto_update to run using this cloud setup
|
22
|
+
# script yet, it updates but does not restart the process.
|
23
|
+
|
24
|
+
su ubuntu -c 'export PATH="$HOME/.gem/bin:$PATH"; export GEM_HOME="$HOME/.gem"; testbot --runner --ssh_tunnel --auto_update --connect your_server_host'
|
25
|
+
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper.rb'))
|
2
|
+
|
3
|
+
describe TestbotCloud::Cli do
|
4
|
+
|
5
|
+
it "should include Thor::Actions" do
|
6
|
+
TestbotCloud::Cli.included_modules.should include(Thor::Actions)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should set source_root to lib/templates" do
|
10
|
+
TestbotCloud::Cli.source_root.should include("lib/templates")
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "when calling new with a project name" do
|
14
|
+
|
15
|
+
it "should generate a project" do
|
16
|
+
cli = TestbotCloud::Cli.new
|
17
|
+
cli.should_receive(:copy_file).with("config.yml", "app_name/config.yml")
|
18
|
+
cli.should_receive(:copy_file).with("gitignore", "app_name/.gitignore")
|
19
|
+
cli.should_receive(:copy_file).with("runner.sh", "app_name/bootstrap/runner.sh")
|
20
|
+
cli.new("app_name")
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "when calling start" do
|
26
|
+
|
27
|
+
it "should start a cluster" do
|
28
|
+
TestbotCloud::Cluster.stub!(:new).and_return(cluster = mock(Object))
|
29
|
+
cluster.should_receive(:start)
|
30
|
+
TestbotCloud::Cli.new.start
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when calling stop" do
|
36
|
+
|
37
|
+
it "should stop a cluster" do
|
38
|
+
TestbotCloud::Cluster.stub!(:new).and_return(cluster = mock(Object))
|
39
|
+
cluster.should_receive(:stop)
|
40
|
+
TestbotCloud::Cli.new.stop
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "when calling version" do
|
46
|
+
|
47
|
+
it "should print the version" do
|
48
|
+
cli = TestbotCloud::Cli.new
|
49
|
+
cli.should_receive(:puts).with("TestbotCloud #{TestbotCloud::VERSION}")
|
50
|
+
cli.version
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper.rb'))
|
2
|
+
|
3
|
+
describe TestbotCloud::Cluster do
|
4
|
+
|
5
|
+
describe "when not in a project" do
|
6
|
+
|
7
|
+
it "should do nothing if there is no config.yml file" do
|
8
|
+
File.should_receive(:exists?).any_number_of_times.with("config.yml").and_return(false)
|
9
|
+
cluster = TestbotCloud::Cluster.new
|
10
|
+
cluster.start
|
11
|
+
cluster.stop
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "when calling start" do
|
17
|
+
|
18
|
+
before do
|
19
|
+
File.stub!(:exists?).with("config.yml").and_return(true)
|
20
|
+
YAML.should_receive(:load_file).with("config.yml").and_return({
|
21
|
+
"runners" => 2,
|
22
|
+
"ssh_user" => "ubuntu",
|
23
|
+
"provider" => {
|
24
|
+
"provider" => "AWS",
|
25
|
+
"aws_access_key_id" => "KEY_ID"
|
26
|
+
},
|
27
|
+
"runner" => {
|
28
|
+
"image_id" => "ami-xxxx"
|
29
|
+
}
|
30
|
+
});
|
31
|
+
|
32
|
+
FileUtils.stub!(:mkdir_p)
|
33
|
+
Fog::Compute.should_receive(:new).with({ :provider => "AWS",
|
34
|
+
:aws_access_key_id => "KEY_ID" }).
|
35
|
+
and_return(@compute = mock(Object))
|
36
|
+
@compute.stub!(:servers).and_return(mock(Object))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should create servers based on config.yml" do
|
40
|
+
@compute.servers.should_receive(:create).twice.with(:image_id => "ami-xxxx").
|
41
|
+
and_return(fog_server = mock(Object, :id => nil, :wait_for => nil))
|
42
|
+
|
43
|
+
TestbotCloud::Server::Factory.should_receive(:create).twice.with(@compute, { :ssh_user => 'ubuntu' }, fog_server).
|
44
|
+
and_return(server = mock(Object))
|
45
|
+
server.should_receive(:bootstrap!).twice.and_return(true)
|
46
|
+
|
47
|
+
cluster = TestbotCloud::Cluster.new
|
48
|
+
cluster.stub!(:puts)
|
49
|
+
cluster.start
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should log the servers that are created" do
|
53
|
+
@compute.servers.stub!(:create).and_return(mock(Object, :id => "srv-boo", :wait_for => nil),
|
54
|
+
mock(Object, :id => "srv-doo", :wait_for => nil))
|
55
|
+
|
56
|
+
TestbotCloud::Server::Factory.should_receive(:create).twice.and_return(server = mock(Object))
|
57
|
+
server.stub!(:bootstrap!).and_return(true)
|
58
|
+
|
59
|
+
FileUtils.should_receive(:mkdir_p).with(".servers/srv-boo")
|
60
|
+
FileUtils.should_receive(:mkdir_p).with(".servers/srv-doo")
|
61
|
+
|
62
|
+
cluster = TestbotCloud::Cluster.new
|
63
|
+
cluster.stub!(:puts)
|
64
|
+
cluster.stub!(:sleep)
|
65
|
+
cluster.start
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should destroy the server if bootstrap fails" do
|
69
|
+
@compute.servers.stub!(:create).and_return(fog_server0 = mock(Object, :id => "srv-boo", :wait_for => nil),
|
70
|
+
fog_server1 = mock(Object, :id => "srv-moo", :wait_for => nil))
|
71
|
+
|
72
|
+
TestbotCloud::Server::Factory.should_receive(:create).twice.and_return(server = mock(Object))
|
73
|
+
server.stub!(:bootstrap!).and_return(false)
|
74
|
+
|
75
|
+
FileUtils.should_receive(:rm_rf).with(".servers/srv-moo")
|
76
|
+
FileUtils.should_receive(:rm_rf).with(".servers/srv-boo")
|
77
|
+
|
78
|
+
fog_server0.should_receive(:destroy)
|
79
|
+
fog_server1.should_receive(:destroy)
|
80
|
+
|
81
|
+
cluster = TestbotCloud::Cluster.new
|
82
|
+
cluster.stub!(:puts)
|
83
|
+
cluster.start
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should retry server create if it fails with a socket error" do
|
87
|
+
class OpenStructWithQuietId < OpenStruct; def id; end; end
|
88
|
+
class SocketErrorFogServerCollection
|
89
|
+
attr_reader :id
|
90
|
+
|
91
|
+
def create(opts)
|
92
|
+
unless @called_once
|
93
|
+
@called_once = true
|
94
|
+
raise Excon::Errors::SocketError.new(Exception.new)
|
95
|
+
else
|
96
|
+
return OpenStructWithQuietId.new
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@compute.stub!(:servers).and_return(SocketErrorFogServerCollection.new)
|
102
|
+
|
103
|
+
TestbotCloud::Server::Factory.should_receive(:create).twice.
|
104
|
+
and_return(server = mock(Object))
|
105
|
+
server.stub!(:bootstrap!).and_return(true)
|
106
|
+
|
107
|
+
cluster = TestbotCloud::Cluster.new
|
108
|
+
cluster.stub!(:puts)
|
109
|
+
cluster.stub!(:sleep)
|
110
|
+
cluster.start
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should retry wait for ready if it fails with a socket error" do
|
114
|
+
class SocketErrorFogServer
|
115
|
+
attr_reader :id
|
116
|
+
|
117
|
+
def wait_for
|
118
|
+
unless @called_once
|
119
|
+
@called_once = true
|
120
|
+
raise Excon::Errors::SocketError.new(Exception.new)
|
121
|
+
else
|
122
|
+
return true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
@compute.servers.stub!(:create).and_return(fog_server = SocketErrorFogServer.new)
|
128
|
+
|
129
|
+
TestbotCloud::Server::Factory.should_receive(:create).twice.with(@compute,
|
130
|
+
{ :ssh_user => "ubuntu" }, fog_server).
|
131
|
+
and_return(server = mock(Object))
|
132
|
+
server.stub!(:bootstrap!).and_return(true)
|
133
|
+
|
134
|
+
cluster = TestbotCloud::Cluster.new
|
135
|
+
cluster.stub!(:puts)
|
136
|
+
cluster.stub!(:sleep)
|
137
|
+
cluster.start
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should retry bootstrap if it fails with a socket error" do
|
141
|
+
@compute.servers.stub!(:create).and_return(mock(Object, :id => nil, :wait_for => nil))
|
142
|
+
|
143
|
+
class SocketErrorServer
|
144
|
+
def bootstrap!(mutex)
|
145
|
+
unless @called_once
|
146
|
+
@called_once = true
|
147
|
+
raise Excon::Errors::SocketError.new(Exception.new)
|
148
|
+
else
|
149
|
+
return true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
TestbotCloud::Server::Factory.stub!(:create).and_return(SocketErrorServer.new)
|
155
|
+
|
156
|
+
cluster = TestbotCloud::Cluster.new
|
157
|
+
cluster.stub!(:puts)
|
158
|
+
cluster.stub!(:sleep)
|
159
|
+
cluster.start
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "when calling stop" do
|
165
|
+
|
166
|
+
before do
|
167
|
+
File.stub!(:exists?).with("config.yml").and_return(true)
|
168
|
+
YAML.should_receive(:load_file).with("config.yml").and_return({
|
169
|
+
"provider" => {
|
170
|
+
"provider" => "AWS",
|
171
|
+
"aws_access_key_id" => "KEY_ID"
|
172
|
+
},
|
173
|
+
"runner" => {},
|
174
|
+
"runners" => 0
|
175
|
+
});
|
176
|
+
|
177
|
+
Fog::Compute.should_receive(:new).with({ :provider => "AWS",
|
178
|
+
:aws_access_key_id => "KEY_ID" }).
|
179
|
+
and_return(@compute = mock(Object))
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should stop servers that are listed in the log file and ready" do
|
183
|
+
@compute.stub!(:servers).and_return([ mock(Object, :ready? => false, :id => "srv-boo"),
|
184
|
+
server = mock(Object, :ready? => true, :id => "srv-moo"),
|
185
|
+
unrelated_server = mock(Object, :ready? => true, :id => "srv-ahh") ])
|
186
|
+
|
187
|
+
File.should_receive(:exists?).with(".servers/srv-boo").and_return(true)
|
188
|
+
File.should_receive(:exists?).with(".servers/srv-moo").and_return(true)
|
189
|
+
File.should_receive(:exists?).with(".servers/srv-ahh").and_return(false)
|
190
|
+
FileUtils.should_receive(:rm_rf).with(".servers/srv-moo")
|
191
|
+
server.should_receive(:destroy)
|
192
|
+
|
193
|
+
cluster = TestbotCloud::Cluster.new
|
194
|
+
cluster.stub!(:puts)
|
195
|
+
cluster.stop
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should retry destroy if it fails with a socket error" do
|
199
|
+
class SocketErrorServer
|
200
|
+
def id; "srv-moo"; end
|
201
|
+
def ready?; true; end
|
202
|
+
|
203
|
+
def destroy
|
204
|
+
return if @called_once
|
205
|
+
@called_once = true
|
206
|
+
raise Excon::Errors::SocketError.new(Exception.new)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
@compute.stub!(:servers).and_return([ server = SocketErrorServer.new ])
|
211
|
+
|
212
|
+
File.should_receive(:exists?).with(".servers/srv-moo").and_return(true)
|
213
|
+
FileUtils.should_receive(:rm_rf).with(".servers/srv-moo")
|
214
|
+
|
215
|
+
cluster = TestbotCloud::Cluster.new
|
216
|
+
cluster.stub!(:puts)
|
217
|
+
cluster.stub!(:sleep)
|
218
|
+
cluster.stop
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper.rb'))
|
2
|
+
|
3
|
+
describe TestbotCloud::Server::AWS, "bootstrap!" do
|
4
|
+
|
5
|
+
it "should run bootstrap" do
|
6
|
+
fog_server = mock(:dns_name => "example.com", :id => "i-500")
|
7
|
+
|
8
|
+
TestbotCloud::Server::Bootstrap.should_receive(:new).with("example.com", "i-500",
|
9
|
+
:ssh_opts => "-i testbot.pem",
|
10
|
+
:ssh_user => "ubuntu").
|
11
|
+
and_return(bootstrap = mock)
|
12
|
+
bootstrap.should_receive(:install)
|
13
|
+
server = TestbotCloud::Server::AWS.new(nil, { :ssh_user => "ubuntu" }, fog_server)
|
14
|
+
server.bootstrap!(nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper.rb'))
|
2
|
+
|
3
|
+
describe TestbotCloud::Server::Brightbox, "bootstrap!" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
TestbotCloud::Server::Bootstrap.stub!(:new).and_return(mock(:install => true))
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should find unmapped ips and map to the server" do
|
10
|
+
compute = mock(Object, :cloud_ips =>
|
11
|
+
[ mock(Object, :status => "mapped"),
|
12
|
+
available_ip = mock(Object, :status => "unmapped", :public_ip => nil) ])
|
13
|
+
fog_server = mock(Object, :interfaces => [ { "id" => "int-xxyyy" } ], :id => nil)
|
14
|
+
server = TestbotCloud::Server::Brightbox.new(compute, {}, fog_server)
|
15
|
+
|
16
|
+
available_ip.should_receive(:map).with("int-xxyyy")
|
17
|
+
server.bootstrap!(Mutex.new)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should create a cloud ip and map when none are available" do
|
21
|
+
# Found no good way to assert the order of events other than creating a custom mock class.
|
22
|
+
class ComputeWithNoFreeIps
|
23
|
+
def initialize; @cloud_ips = []; end
|
24
|
+
attr_reader :cloud_ips
|
25
|
+
|
26
|
+
def create_cloud_ip
|
27
|
+
@cloud_ips << OpenStruct.new(:status => "unmapped")
|
28
|
+
@cloud_ips.last.should_receive(:map).with("int-xxyyy")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
compute = ComputeWithNoFreeIps.new
|
33
|
+
fog_server = mock(Object, :interfaces => [ { "id" => "int-xxyyy" } ], :id => nil)
|
34
|
+
server = TestbotCloud::Server::Brightbox.new(compute, {}, fog_server)
|
35
|
+
|
36
|
+
server.bootstrap!(Mutex.new)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper.rb'))
|
2
|
+
|
3
|
+
describe TestbotCloud::Server::Factory do
|
4
|
+
|
5
|
+
it "should create a server wrapper for Brightbox" do
|
6
|
+
server = TestbotCloud::Server::Factory.create(nil, nil, Fog::Brightbox::Compute::Server.new)
|
7
|
+
server.should be_instance_of(TestbotCloud::Server::Brightbox)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should create a server wrapper for AWS" do
|
11
|
+
server = TestbotCloud::Server::Factory.create(nil, nil, Fog::AWS::Compute::Server.new)
|
12
|
+
server.should be_instance_of(TestbotCloud::Server::AWS)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise an error when there is no server wrapper" do
|
16
|
+
expect {
|
17
|
+
TestbotCloud::Server::Factory.create(nil, nil, Fog::Slicehost::Compute::Server.new)
|
18
|
+
}.to raise_error(/Unsupported server type: /)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../spec_helper.rb'))
|
2
|
+
|
3
|
+
describe TestbotCloud::Server::Bootstrap do
|
4
|
+
|
5
|
+
it "should be able to upload bootstrap files and run them" do
|
6
|
+
bootstrap = TestbotCloud::Server::Bootstrap.new("15.14.13.12", "srv-foo", { :ssh_user => "ubuntu" })
|
7
|
+
|
8
|
+
bootstrap.should_receive(:system).and_return(true)
|
9
|
+
bootstrap.should_receive(:system).with("scp -o StrictHostKeyChecking=no -r bootstrap ubuntu@15.14.13.12:~ &> /dev/null").and_return(true)
|
10
|
+
bootstrap.should_receive(:system).with("ssh -o StrictHostKeyChecking=no ubuntu@15.14.13.12 'cd bootstrap; sudo sh runner.sh' &> /dev/null").and_return(true)
|
11
|
+
|
12
|
+
bootstrap.stub!(:sleep)
|
13
|
+
bootstrap.install
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should try to get a connection going before running bootstrap" do
|
17
|
+
bootstrap = TestbotCloud::Server::Bootstrap.new("15.14.13.12", "srv-foo", { :ssh_user => "ubuntu" })
|
18
|
+
|
19
|
+
bootstrap.should_receive(:system).and_return(false)
|
20
|
+
bootstrap.should_receive(:system).and_return(false)
|
21
|
+
bootstrap.should_receive(:system).and_return(true)
|
22
|
+
bootstrap.should_receive(:system).with("scp -o StrictHostKeyChecking=no -r bootstrap ubuntu@15.14.13.12:~ &> /dev/null").and_return(true)
|
23
|
+
bootstrap.should_receive(:system).with("ssh -o StrictHostKeyChecking=no ubuntu@15.14.13.12 'cd bootstrap; sudo sh runner.sh' &> /dev/null").and_return(true)
|
24
|
+
|
25
|
+
bootstrap.stub!(:sleep).any_number_of_times
|
26
|
+
bootstrap.install
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not try to bootstrap if the connection fails" do
|
30
|
+
bootstrap = TestbotCloud::Server::Bootstrap.new("15.14.13.12", "srv-foo", { :ssh_user => "ubuntu" })
|
31
|
+
bootstrap.stub!(:system).and_return(false)
|
32
|
+
bootstrap.should_not_receive(:system).with("scp -o StrictHostKeyChecking=no -r bootstrap ubuntu@15.14.13.12:~ &> /dev/null")
|
33
|
+
|
34
|
+
bootstrap.stub!(:sleep)
|
35
|
+
bootstrap.stub!(:puts)
|
36
|
+
bootstrap.install
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/testbot_cloud.rb'))
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "testbot_cloud/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "testbot_cloud"
|
7
|
+
s.version = TestbotCloud::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Joakim Kolsjö"]
|
10
|
+
s.email = ["joakim.kolsjo@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Run your tests in the cloud}
|
13
|
+
s.description = %q{A tool for creating and managing testbot clusters in the cloud}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency "thor", ">0.14.5"
|
21
|
+
s.add_dependency "fog", ">0.7.1"
|
22
|
+
s.add_dependency "activesupport", ">3.0.0"
|
23
|
+
s.add_development_dependency "bundler"
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
s.add_development_dependency "cucumber"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: testbot_cloud
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Joakim Kolsj\xC3\xB6"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-17 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thor
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 45
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 14
|
33
|
+
- 5
|
34
|
+
version: 0.14.5
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: fog
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 1
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 7
|
49
|
+
- 1
|
50
|
+
version: 0.7.1
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: activesupport
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 7
|
62
|
+
segments:
|
63
|
+
- 3
|
64
|
+
- 0
|
65
|
+
- 0
|
66
|
+
version: 3.0.0
|
67
|
+
type: :runtime
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
type: :development
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: cucumber
|
99
|
+
prerelease: false
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
type: :development
|
110
|
+
version_requirements: *id006
|
111
|
+
description: A tool for creating and managing testbot clusters in the cloud
|
112
|
+
email:
|
113
|
+
- joakim.kolsjo@gmail.com
|
114
|
+
executables:
|
115
|
+
- testbot_cloud
|
116
|
+
extensions: []
|
117
|
+
|
118
|
+
extra_rdoc_files: []
|
119
|
+
|
120
|
+
files:
|
121
|
+
- .autotest
|
122
|
+
- .gemtest
|
123
|
+
- .gitignore
|
124
|
+
- .rvmrc
|
125
|
+
- CHANGELOG
|
126
|
+
- Gemfile
|
127
|
+
- README.markdown
|
128
|
+
- Rakefile
|
129
|
+
- autotest/discover.rb
|
130
|
+
- bin/testbot_cloud
|
131
|
+
- features/cluster_management.feature
|
132
|
+
- features/project_creation.feature
|
133
|
+
- features/step_definitions/shared_steps.rb
|
134
|
+
- lib/cli.rb
|
135
|
+
- lib/cluster.rb
|
136
|
+
- lib/server/aws.rb
|
137
|
+
- lib/server/brightbox.rb
|
138
|
+
- lib/server/factory.rb
|
139
|
+
- lib/server/shared/bootstrap.rb
|
140
|
+
- lib/servers.rb
|
141
|
+
- lib/templates/config.yml
|
142
|
+
- lib/templates/gitignore
|
143
|
+
- lib/templates/runner.sh
|
144
|
+
- lib/testbot_cloud.rb
|
145
|
+
- lib/testbot_cloud/version.rb
|
146
|
+
- spec/cli_spec.rb
|
147
|
+
- spec/cluster_spec.rb
|
148
|
+
- spec/server/aws_spec.rb
|
149
|
+
- spec/server/brightbox_spec.rb
|
150
|
+
- spec/server/factory_spec.rb
|
151
|
+
- spec/server/shared/bootstrap_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
153
|
+
- testbot_cloud.gemspec
|
154
|
+
has_rdoc: true
|
155
|
+
homepage: ""
|
156
|
+
licenses: []
|
157
|
+
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
|
161
|
+
require_paths:
|
162
|
+
- lib
|
163
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
164
|
+
none: false
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
hash: 3
|
169
|
+
segments:
|
170
|
+
- 0
|
171
|
+
version: "0"
|
172
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
hash: 3
|
178
|
+
segments:
|
179
|
+
- 0
|
180
|
+
version: "0"
|
181
|
+
requirements: []
|
182
|
+
|
183
|
+
rubyforge_project:
|
184
|
+
rubygems_version: 1.5.3
|
185
|
+
signing_key:
|
186
|
+
specification_version: 3
|
187
|
+
summary: Run your tests in the cloud
|
188
|
+
test_files: []
|
189
|
+
|