breeze 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +12 -6
- data/breeze.gemspec +1 -1
- data/features/getting_started.feature +23 -0
- data/features/step_definitions/application_steps.rb +14 -0
- data/features/step_definitions/server_steps.rb +8 -0
- data/features/support/env.rb +10 -5
- data/lib/breeze.rb +1 -1
- data/lib/breeze/fog_extensions/aws.rb +5 -5
- data/lib/breeze/fog_wrapper.rb +73 -0
- data/lib/breeze/tasks/server.rb +5 -3
- data/lib/breeze/veur.rb +11 -7
- metadata +69 -83
data/README.md
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
# breeze
|
2
2
|
|
3
3
|
Breeze provides some [Thor](https://github.com/wycats/thor) tasks and example scripts for managing cloud computing resources
|
4
|
-
and deployments
|
5
|
-
work so it should be fairly easy to add support for other cloud computing
|
4
|
+
and deployments. Currently only [Amazon's AWS cloud](http://aws.amazon.com/) is supported, but breeze uses
|
5
|
+
[fog](https://github.com/geemus/fog) for the hard work so it should be fairly easy to add support for other cloud computing
|
6
|
+
providers that are supported by fog.
|
6
7
|
|
7
|
-
Breeze implements zero downtime
|
8
|
-
compatible with the previous version.
|
8
|
+
Breeze implements zero downtime deploys and rollbacks by moving an elastic ip from one server to another. Db migrations have to be
|
9
|
+
compatible with the previous version. http://pedro.herokuapp.com/past/2011/7/13/rails_migrations_with_no_downtime/
|
9
10
|
|
10
11
|
## install
|
11
12
|
|
13
|
+
Run the following commands to install breeze and initialize a new project with configuration file templates.
|
14
|
+
|
12
15
|
gem install breeze
|
13
16
|
cd your-project
|
14
17
|
breeze init
|
@@ -21,10 +24,13 @@ for more information.
|
|
21
24
|
|
22
25
|
## create a server image
|
23
26
|
|
27
|
+
The command below installs a server and saves a private server image. When this is fully automated it can be
|
28
|
+
repeated with new software packages or a new OS version.
|
29
|
+
|
24
30
|
thor server:image:create
|
25
31
|
|
26
32
|
The default install.sh compiles ruby, passenger, nginx and image magick. It takes a long time
|
27
|
-
(maybe half an hour on a small instance)
|
33
|
+
(maybe half an hour on a small instance) and it will prompt for the image name when completed.
|
28
34
|
|
29
35
|
## use it
|
30
36
|
|
@@ -37,7 +43,7 @@ The default install.sh compiles ruby, passenger, nginx and image magick. It take
|
|
37
43
|
thor staging:start # Start web server and db for staging
|
38
44
|
thor staging:stop # Stop staging and destroy server and db
|
39
45
|
|
40
|
-
Define your staging and production in the Thorfile and the same tasks become available for both name spaces.
|
46
|
+
Define your staging and production constants in the Thorfile and the same tasks become available for both name spaces.
|
41
47
|
These tasks call app tasks with fixed parameters.
|
42
48
|
|
43
49
|
## plumbing commands
|
data/breeze.gemspec
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
Feature: Getting started
|
2
|
+
|
3
|
+
As a new user I want to quickly get an idea of how breeze works.
|
4
|
+
|
5
|
+
Scenario: Start a new project
|
6
|
+
Given I have an empty working directory
|
7
|
+
When I run `breeze init`
|
8
|
+
Then a file named "Thorfile" should exist
|
9
|
+
|
10
|
+
Scenario: Start a new server
|
11
|
+
Given my Thorfile contains access credentials and configuration
|
12
|
+
When I run `thor server:create`
|
13
|
+
And I run `thor describe:servers`
|
14
|
+
Then the output should look like:
|
15
|
+
"""
|
16
|
+
=== SERVER INSTANCES ===========================================================================
|
17
|
+
Name Instance ID IP Address .* Image ID Type Zone State
|
18
|
+
YOUR-PRIVATE-AMI-OR-A-PUBLIC-ONE t1.micro us-east-1a running
|
19
|
+
"""
|
20
|
+
|
21
|
+
Scenario: Terminate a server
|
22
|
+
Given I have started a server
|
23
|
+
Then I can terminate the server with `thor server:destroy [SERVER ID] --force`
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Given /^I have an empty working directory$/ do
|
2
|
+
step "I run `rm -rf config Thorfile`"
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^my Thorfile contains access credentials and configuration$/ do
|
6
|
+
# the Thorfile should be okay already, just check it
|
7
|
+
check_file_content('Thorfile', "CONFIGURATION", true)
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^the output should look like:$/ do |lines|
|
11
|
+
lines.each_line do |line|
|
12
|
+
all_output.should match(/#{line.strip}/)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Given /^I have started a server$/ do
|
2
|
+
step "I run `thor server:create`"
|
3
|
+
@started_server_id = $1 if all_stdout =~ /server (i-[^.])\.\.\./
|
4
|
+
end
|
5
|
+
|
6
|
+
Then /^I can terminate the server with `thor server:destroy \[SERVER ID\] \-\-force`$/ do
|
7
|
+
step "I successfully run `thor server:destroy #{@started_server_id} --force`"
|
8
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -16,13 +16,17 @@ END_SCRIPT
|
|
16
16
|
# and commands that make changes to the local system.
|
17
17
|
system("rm -rf #{template_dir}/config/breeze/configs/*")
|
18
18
|
|
19
|
-
# Use the current source insted of the installed gem.
|
19
|
+
# Use the current source with bundler insted of the installed gem.
|
20
20
|
thorfile_path = File.join(template_dir, 'Thorfile')
|
21
21
|
thorfile_content = File.read(thorfile_path)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
{ # replace thorfile content:
|
23
|
+
"require 'breeze'" => "require 'bundler'; Bundler.setup; require 'breeze'",
|
24
|
+
"'THE-NAME-OF-YOUR-KEYPAIR'" => 'nil'
|
25
|
+
}.each do |expected, wanted|
|
26
|
+
raise "Cannot find #{expected} in #{thorfile_path}" unless thorfile_content.include?(expected)
|
27
|
+
thorfile_content.sub!(expected, wanted)
|
28
|
+
end
|
29
|
+
File.open(thorfile_path, 'w') { |f| f.puts(thorfile_content) }
|
26
30
|
|
27
31
|
# Use Fog.mock!
|
28
32
|
system("echo 'Fog.mock!' >> #{thorfile_path}")
|
@@ -30,4 +34,5 @@ system("echo 'Fog.mock!' >> #{thorfile_path}")
|
|
30
34
|
# Clone the test app for each scenario.
|
31
35
|
Before do
|
32
36
|
system("cp -r #{template_dir} tmp/aruba")
|
37
|
+
@aruba_timeout_seconds = 5
|
33
38
|
end
|
data/lib/breeze.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'fog/
|
2
|
-
require 'fog/
|
1
|
+
require 'fog/aws/models/compute/server'
|
2
|
+
require 'fog/aws/models/compute/image'
|
3
3
|
|
4
4
|
module Fog
|
5
5
|
|
6
|
-
module
|
7
|
-
class
|
6
|
+
module Compute
|
7
|
+
class AWS::Server
|
8
8
|
|
9
9
|
def name
|
10
10
|
breeze_data['name'] || tags['Name']
|
@@ -54,7 +54,7 @@ module Fog
|
|
54
54
|
end
|
55
55
|
|
56
56
|
end
|
57
|
-
class
|
57
|
+
class AWS::Image
|
58
58
|
|
59
59
|
def display_name
|
60
60
|
name or location
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'breeze/fog_extensions'
|
2
|
+
module Breeze
|
3
|
+
|
4
|
+
# The fog wrapper makes it possible for subsequent shell commands
|
5
|
+
# to share the same fog mock session. If Fog.mocking? is true, the
|
6
|
+
# mock data is read from and written to a yaml file.
|
7
|
+
module FogWrapper
|
8
|
+
|
9
|
+
def self.connection(type)
|
10
|
+
{:compute => Compute, :rds => RDS}[type].get_connection
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.flush_mock_data!
|
14
|
+
Compute.new.flush_data!
|
15
|
+
# RDS.new.flush_data!
|
16
|
+
end
|
17
|
+
|
18
|
+
class AbstractConnectionWrapper
|
19
|
+
|
20
|
+
def self.get_connection
|
21
|
+
Fog.mocking? ? new : direct_fog_connection
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(*args)
|
25
|
+
load_data
|
26
|
+
return_value = fog.send(*args)
|
27
|
+
flush_data!
|
28
|
+
return_value
|
29
|
+
end
|
30
|
+
|
31
|
+
def flush_data!
|
32
|
+
File.open(data_file, 'w') { |f| YAML::dump(get_data, f) }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def fog
|
38
|
+
@fog ||= self.class.direct_fog_connection
|
39
|
+
end
|
40
|
+
def load_data
|
41
|
+
set_data(YAML::load_file(data_file)) if File.exists?(data_file)
|
42
|
+
end
|
43
|
+
def get_data
|
44
|
+
mock_class.instance_variable_get('@data')
|
45
|
+
end
|
46
|
+
def set_data(data)
|
47
|
+
mock_class.instance_variable_set('@data', data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Compute < AbstractConnectionWrapper
|
52
|
+
def self.direct_fog_connection
|
53
|
+
Fog::Compute.new(CONFIGURATION[:cloud_service])
|
54
|
+
end
|
55
|
+
private
|
56
|
+
def data_file ; 'fog_compute_data.yaml' ; end
|
57
|
+
def mock_class ; Fog::Compute::AWS::Mock ; end
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO: add RDS mocks to fog so that we can start testing it
|
61
|
+
class RDS < AbstractConnectionWrapper
|
62
|
+
def self.direct_fog_connection
|
63
|
+
credentials = CONFIGURATION[:cloud_service].reject{ |k,v| k == :provider }
|
64
|
+
credentials[:region] = CONFIGURATION[:db_region]
|
65
|
+
Fog::AWS::RDS.new(credentials)
|
66
|
+
end
|
67
|
+
private
|
68
|
+
def data_file ; 'fog_rds_data.yaml' ; end
|
69
|
+
def mock_class ; Fog::AWS::RDS::Mock ; end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
data/lib/breeze/tasks/server.rb
CHANGED
@@ -33,17 +33,19 @@ module Breeze
|
|
33
33
|
server = fog.servers.create(options)
|
34
34
|
print "Launching server #{server.id}"
|
35
35
|
wait_until('running!') { server.running? }
|
36
|
+
FogWrapper.flush_mock_data! if Fog.mocking?
|
36
37
|
return server
|
37
38
|
end
|
38
39
|
|
39
40
|
# Can take a host name or an ip address. Resolves the host name
|
40
41
|
# and returns the ip address if get_ip is passed in as true.
|
41
42
|
def wait_until_host_is_available(host, get_ip=false)
|
42
|
-
|
43
|
+
resolved_host = Resolv.getaddresses(host).first
|
44
|
+
if resolved_host.nil?
|
43
45
|
print("Waiting for #{host} to resolve")
|
44
|
-
wait_until('ready!') { Resolv.getaddresses(host).
|
46
|
+
wait_until('ready!') { resolved_host = Resolv.getaddresses(host).first }
|
45
47
|
end
|
46
|
-
host =
|
48
|
+
host = resolved_host if get_ip
|
47
49
|
unless remote_is_available?(host)
|
48
50
|
print("Waiting for #{host} to accept connections")
|
49
51
|
wait_until('ready!') { remote_is_available?(host) }
|
data/lib/breeze/veur.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'breeze/
|
1
|
+
require 'breeze/fog_wrapper'
|
2
2
|
|
3
3
|
module Breeze
|
4
4
|
|
@@ -38,7 +38,14 @@ module Breeze
|
|
38
38
|
# wait_until { my_task.completed? }
|
39
39
|
def wait_until(message='completed!')
|
40
40
|
3.times { dot_and_sleep(1) }
|
41
|
-
|
41
|
+
begin
|
42
|
+
dot_and_sleep(2) until yield
|
43
|
+
rescue Excon::Errors::SocketError => e
|
44
|
+
# print out the error so the user can interrupt if necessary
|
45
|
+
print "#{e.class}: #{e.message}! Retry:"
|
46
|
+
sleep(1)
|
47
|
+
retry
|
48
|
+
end
|
42
49
|
puts message
|
43
50
|
end
|
44
51
|
|
@@ -68,7 +75,7 @@ module Breeze
|
|
68
75
|
end
|
69
76
|
|
70
77
|
def fog
|
71
|
-
@fog ||=
|
78
|
+
@fog ||= Breeze::FogWrapper.connection(:compute)
|
72
79
|
end
|
73
80
|
|
74
81
|
def dns
|
@@ -76,10 +83,7 @@ module Breeze
|
|
76
83
|
end
|
77
84
|
|
78
85
|
def rds
|
79
|
-
|
80
|
-
credentials = CONFIGURATION[:cloud_service].reject{ |k,v| k == :provider }
|
81
|
-
credentials[:region] = CONFIGURATION[:db_region]
|
82
|
-
@rds = Fog::AWS::RDS.new(credentials)
|
86
|
+
@rds ||= Breeze::FogWrapper.connection(:rds)
|
83
87
|
end
|
84
88
|
|
85
89
|
end
|
metadata
CHANGED
@@ -1,89 +1,76 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: breeze
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 4
|
9
|
-
version: 0.0.4
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Markus Bengts
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-01-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: thor
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70318976130880 !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
- 0
|
30
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
31
22
|
type: :runtime
|
32
|
-
version_requirements: *id001
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: fog
|
35
23
|
prerelease: false
|
36
|
-
|
24
|
+
version_requirements: *70318976130880
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: fog
|
27
|
+
requirement: &70318976130460 !ruby/object:Gem::Requirement
|
37
28
|
none: false
|
38
|
-
requirements:
|
39
|
-
- -
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
|
42
|
-
- 0
|
43
|
-
- 7
|
44
|
-
version: "0.7"
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
45
33
|
type: :runtime
|
46
|
-
version_requirements: *id002
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: cucumber
|
49
34
|
prerelease: false
|
50
|
-
|
35
|
+
version_requirements: *70318976130460
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: cucumber
|
38
|
+
requirement: &70318976146200 !ruby/object:Gem::Requirement
|
51
39
|
none: false
|
52
|
-
requirements:
|
53
|
-
- -
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
|
56
|
-
- 0
|
57
|
-
version: "0"
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
58
44
|
type: :development
|
59
|
-
version_requirements: *id003
|
60
|
-
- !ruby/object:Gem::Dependency
|
61
|
-
name: aruba
|
62
45
|
prerelease: false
|
63
|
-
|
46
|
+
version_requirements: *70318976146200
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: aruba
|
49
|
+
requirement: &70318976145400 !ruby/object:Gem::Requirement
|
64
50
|
none: false
|
65
|
-
requirements:
|
66
|
-
- -
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
|
69
|
-
- 0
|
70
|
-
version: "0"
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
71
55
|
type: :development
|
72
|
-
|
73
|
-
|
74
|
-
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70318976145400
|
58
|
+
description: ! 'Breeze makes it easy to automate server installation and configuration.
|
59
|
+
It provides
|
60
|
+
|
75
61
|
example scripts and configuration files that you can modify and keep in your revision
|
76
|
-
control system. Thor tasks are provided to create server images, launch server instances etc.
|
77
62
|
|
78
|
-
|
63
|
+
control system. Thor tasks are provided to create server images, launch server instances
|
64
|
+
etc.
|
65
|
+
|
66
|
+
'
|
67
|
+
email:
|
79
68
|
- markus.bengts@gmail.com
|
80
|
-
executables:
|
69
|
+
executables:
|
81
70
|
- breeze
|
82
71
|
extensions: []
|
83
|
-
|
84
72
|
extra_rdoc_files: []
|
85
|
-
|
86
|
-
files:
|
73
|
+
files:
|
87
74
|
- .gitignore
|
88
75
|
- Gemfile
|
89
76
|
- LICENSE
|
@@ -92,10 +79,14 @@ files:
|
|
92
79
|
- bin/breeze
|
93
80
|
- breeze.gemspec
|
94
81
|
- features/erb_conf.feature
|
82
|
+
- features/getting_started.feature
|
83
|
+
- features/step_definitions/application_steps.rb
|
84
|
+
- features/step_definitions/server_steps.rb
|
95
85
|
- features/support/env.rb
|
96
86
|
- lib/breeze.rb
|
97
87
|
- lib/breeze/fog_extensions.rb
|
98
88
|
- lib/breeze/fog_extensions/aws.rb
|
89
|
+
- lib/breeze/fog_wrapper.rb
|
99
90
|
- lib/breeze/initializer.rb
|
100
91
|
- lib/breeze/tasks.rb
|
101
92
|
- lib/breeze/tasks/app.rb
|
@@ -125,38 +116,33 @@ files:
|
|
125
116
|
- lib/templates/shared/scripts/deploy.sh
|
126
117
|
- lib/templates/shared/scripts/install.sh
|
127
118
|
- lib/templates/user_data.sh
|
128
|
-
has_rdoc: true
|
129
119
|
homepage: https://github.com/markus/breeze
|
130
120
|
licenses: []
|
131
|
-
|
132
121
|
post_install_message:
|
133
122
|
rdoc_options: []
|
134
|
-
|
135
|
-
require_paths:
|
123
|
+
require_paths:
|
136
124
|
- lib
|
137
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
126
|
none: false
|
139
|
-
requirements:
|
140
|
-
- -
|
141
|
-
- !ruby/object:Gem::Version
|
142
|
-
|
143
|
-
|
144
|
-
version: "0"
|
145
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
132
|
none: false
|
147
|
-
requirements:
|
148
|
-
- -
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
|
151
|
-
- 0
|
152
|
-
version: "0"
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
153
137
|
requirements: []
|
154
|
-
|
155
138
|
rubyforge_project: breeze
|
156
|
-
rubygems_version: 1.
|
139
|
+
rubygems_version: 1.8.11
|
157
140
|
signing_key:
|
158
141
|
specification_version: 3
|
159
142
|
summary: Thor tasks to manage cloud computing resources and deployments
|
160
|
-
test_files:
|
143
|
+
test_files:
|
161
144
|
- features/erb_conf.feature
|
145
|
+
- features/getting_started.feature
|
146
|
+
- features/step_definitions/application_steps.rb
|
147
|
+
- features/step_definitions/server_steps.rb
|
162
148
|
- features/support/env.rb
|