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