breeze 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/breeze.gemspec +3 -1
- data/features/erb_conf.feature +76 -0
- data/features/support/env.rb +33 -0
- data/lib/breeze.rb +1 -1
- data/lib/breeze/fog_extensions/aws.rb +5 -0
- data/lib/breeze/tasks/app.rb +14 -3
- data/lib/breeze/tasks/configuration.rb +3 -3
- data/lib/breeze/tasks/describe.rb +1 -1
- data/lib/breeze/tasks/server.rb +28 -12
- data/lib/breeze/tasks/server/image.rb +11 -0
- data/lib/templates/Thorfile +4 -7
- data/lib/templates/profiles/rails_and_image_magick/configs/database.yml +1 -1
- data/lib/templates/profiles/rails_and_image_magick/scripts/install_cust.sh +1 -0
- metadata +35 -6
data/.gitignore
CHANGED
data/breeze.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Markus Bengts"]
|
10
10
|
s.email = ["markus.bengts@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/markus/breeze"
|
12
|
-
s.summary = %q{Thor tasks to manage
|
12
|
+
s.summary = %q{Thor tasks to manage cloud computing resources and deployments}
|
13
13
|
s.description = <<-END_DESCRIPTION
|
14
14
|
Breeze makes it easy to automate server installation and configuration. It provides
|
15
15
|
example scripts and configuration files that you can modify and keep in your revision
|
@@ -25,4 +25,6 @@ END_DESCRIPTION
|
|
25
25
|
|
26
26
|
s.add_dependency('thor')
|
27
27
|
s.add_dependency('fog', '>= 0.7')
|
28
|
+
s.add_development_dependency "cucumber"
|
29
|
+
s.add_development_dependency "aruba"
|
28
30
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
Feature: ERb Configuration
|
2
|
+
|
3
|
+
Server configuration files can be transformed from ERb templates
|
4
|
+
in config/breeze/configs and deployed to any path on the server.
|
5
|
+
This allows us to keep configuration files under revision control
|
6
|
+
and to deploy the same configuration to multiple servers.
|
7
|
+
|
8
|
+
Scenario: Transform and deploy a configuration file
|
9
|
+
Given a file named "config/breeze/configs/test" with:
|
10
|
+
"""
|
11
|
+
<%
|
12
|
+
@path = 'test.conf'
|
13
|
+
@perms = 0600
|
14
|
+
@post = 'ls -l test.conf'
|
15
|
+
%>
|
16
|
+
http {
|
17
|
+
root <%= CONFIGURATION[:app_path] %>/public
|
18
|
+
}
|
19
|
+
"""
|
20
|
+
When I run `thor configuration:deploy_to_localhost`
|
21
|
+
Then the file "test.conf" should contain exactly:
|
22
|
+
"""
|
23
|
+
|
24
|
+
http {
|
25
|
+
root /srv/YOUR-APP/public
|
26
|
+
}
|
27
|
+
"""
|
28
|
+
And the output should contain "-rw-------"
|
29
|
+
|
30
|
+
Scenario: Read and write using custom commands
|
31
|
+
Given a file named "config/breeze/configs/test" with:
|
32
|
+
"""
|
33
|
+
<%
|
34
|
+
@read_cmd = 'echo "previous content"'
|
35
|
+
@write_cmd = 'cat'
|
36
|
+
%>
|
37
|
+
new content
|
38
|
+
"""
|
39
|
+
When I run `thor configuration:deploy_to_localhost`
|
40
|
+
Then the output should contain "new content"
|
41
|
+
|
42
|
+
Scenario: Define transformation order with file names
|
43
|
+
Given a file named "config/breeze/configs/a_file" with:
|
44
|
+
"""
|
45
|
+
<%
|
46
|
+
@path = 'ab_test'
|
47
|
+
@perms = 0700
|
48
|
+
%>
|
49
|
+
this file is transformed first and then backed up
|
50
|
+
"""
|
51
|
+
And a file named "config/breeze/configs/b_file" with:
|
52
|
+
"""
|
53
|
+
<%
|
54
|
+
@path = 'ab_test'
|
55
|
+
@perms = 0700
|
56
|
+
@post = 'ls -l ab_test.backup'
|
57
|
+
%>
|
58
|
+
this overwrites the first ab_test
|
59
|
+
"""
|
60
|
+
When I run `thor configuration:deploy_to_localhost`
|
61
|
+
Then the file "ab_test" should contain "this overwrites the first ab_test"
|
62
|
+
And the file "ab_test.backup" should contain "transformed first and then backed up"
|
63
|
+
And the output should contain "-rwx------"
|
64
|
+
|
65
|
+
Scenario: Transform and deploy only one file
|
66
|
+
Given a file named "config/breeze/configs/a_file" with:
|
67
|
+
"""
|
68
|
+
<% @path = 'a_file' %> this is a
|
69
|
+
"""
|
70
|
+
And a file named "config/breeze/configs/b_file" with:
|
71
|
+
"""
|
72
|
+
<% @path = 'b_file' %> this is b
|
73
|
+
"""
|
74
|
+
When I run `thor configuration:deploy_to_localhost config/breeze/configs/b*`
|
75
|
+
Then a file named "b_file" should exist
|
76
|
+
And a file named "a_file" should not exist
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
|
3
|
+
# We need a clean test app template that can be cloned into
|
4
|
+
# tmp/aruba for each scenario.
|
5
|
+
template_dir = File.expand_path('tmp/test_app_template')
|
6
|
+
|
7
|
+
# Create the test app with `breeze init`.
|
8
|
+
system <<-END_SCRIPT
|
9
|
+
rm -rf #{template_dir}
|
10
|
+
mkdir -p #{template_dir}
|
11
|
+
cd #{template_dir}
|
12
|
+
bundle exec ../../bin/breeze init > /dev/null
|
13
|
+
END_SCRIPT
|
14
|
+
|
15
|
+
# Remove all configuration file templates because they may contain absolute paths
|
16
|
+
# and commands that make changes to the local system.
|
17
|
+
system("rm -rf #{template_dir}/config/breeze/configs/*")
|
18
|
+
|
19
|
+
# Use the current source insted of the installed gem.
|
20
|
+
thorfile_path = File.join(template_dir, 'Thorfile')
|
21
|
+
thorfile_content = File.read(thorfile_path)
|
22
|
+
expected_require = "require 'breeze'"
|
23
|
+
wanted_require = "require 'bundler'; Bundler.setup; require 'breeze'"
|
24
|
+
raise "Cannot find #{expected_require} in #{thorfile_path}" unless thorfile_content.include?(expected_require)
|
25
|
+
File.open(thorfile_path, 'w') { |f| f.puts(thorfile_content.sub(expected_require, wanted_require)) }
|
26
|
+
|
27
|
+
# Use Fog.mock!
|
28
|
+
system("echo 'Fog.mock!' >> #{thorfile_path}")
|
29
|
+
|
30
|
+
# Clone the test app for each scenario.
|
31
|
+
Before do
|
32
|
+
system("cp -r #{template_dir} tmp/aruba")
|
33
|
+
end
|
data/lib/breeze.rb
CHANGED
@@ -11,6 +11,7 @@ module Fog
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def display_name
|
14
|
+
return "#{state}:#{name}" if name and state != 'running'
|
14
15
|
name || public_ip_address || "#{state} #{flavor_id} #{id}"
|
15
16
|
end
|
16
17
|
|
@@ -55,6 +56,10 @@ module Fog
|
|
55
56
|
end
|
56
57
|
class Compute::Image
|
57
58
|
|
59
|
+
def display_name
|
60
|
+
name or location
|
61
|
+
end
|
62
|
+
|
58
63
|
def full_type
|
59
64
|
"#{type}, #{architecture}, #{root_device_type}"
|
60
65
|
end
|
data/lib/breeze/tasks/app.rb
CHANGED
@@ -17,9 +17,12 @@ module Breeze
|
|
17
17
|
end
|
18
18
|
server = create_server
|
19
19
|
server.breeze_data(:name => public_server_name, :db => db_server_name)
|
20
|
-
|
21
|
-
|
20
|
+
if options[:elastic_ip]
|
21
|
+
thor("server:address:create #{server.id}")
|
22
|
+
server.reload until server.addresses.first
|
23
|
+
end
|
22
24
|
deploy_command([server], public_server_name, db_server_name, options[:deploy_branch]) if options[:deploy_branch]
|
25
|
+
thor("dns:record:create #{zone_id(public_server_name)} #{public_server_name}. A #{ip(server)} #{options[:dns_ttl]}")
|
23
26
|
end
|
24
27
|
|
25
28
|
desc 'stop PUBLIC_SERVER_NAME', 'Destroy web server and db'
|
@@ -55,7 +58,7 @@ module Breeze
|
|
55
58
|
new_server = create_server
|
56
59
|
new_server.breeze_data(:name => public_server_name, :db => db_server_name)
|
57
60
|
deploy_command([new_server], public_server_name, db_server_name, branch)
|
58
|
-
puts("The new server should soon be available at #{ip(new_server)}
|
61
|
+
puts("The new server should soon be available at: #{ip(new_server)}")
|
59
62
|
if ask("Ready to continue and move the elastic_ip for #{public_server_name} to the new server? [YES/rollback] >") =~ /r|n/i
|
60
63
|
new_server.destroy
|
61
64
|
else
|
@@ -153,6 +156,10 @@ module Breeze
|
|
153
156
|
super(task + (options[:force] ? ' --force' : ''))
|
154
157
|
end
|
155
158
|
|
159
|
+
def log_in_to(server)
|
160
|
+
system("#{CONFIGURATION[:ssh][:ssh_command]} #{CONFIGURATION[:ssh][:ssh_user]}@#{server}")
|
161
|
+
end
|
162
|
+
|
156
163
|
# Don't know how to include or inherit thor tasks and descriptions.
|
157
164
|
# These may be included in Staging and Production.
|
158
165
|
def self.inherited(c)
|
@@ -173,6 +180,10 @@ module Breeze
|
|
173
180
|
def enable
|
174
181
|
thor("app:enable \#{PUBLIC_SERVER_NAME}")
|
175
182
|
end
|
183
|
+
desc 'ssh', 'Log in with ssh'
|
184
|
+
def ssh
|
185
|
+
log_in_to(PUBLIC_SERVER_NAME)
|
186
|
+
end
|
176
187
|
END_TASKS
|
177
188
|
end
|
178
189
|
|
@@ -9,11 +9,11 @@ module Breeze
|
|
9
9
|
# See https://github.com/wr0ngway/rubber/wiki/Configuration
|
10
10
|
class Configuration < Veur
|
11
11
|
|
12
|
-
desc 'deploy_to_localhost',
|
12
|
+
desc 'deploy_to_localhost [FILE]',
|
13
13
|
'Transform and deploy server configuration files to the local file system based on ERB templates in config/server'
|
14
14
|
method_option :force, :default => false, :desc => 'Overwrite and execute @post commands even if files would not change'
|
15
|
-
def deploy_to_localhost
|
16
|
-
Dir[
|
15
|
+
def deploy_to_localhost(file_pattern='config/breeze/configs/**/*')
|
16
|
+
Dir[file_pattern].sort.each do |path|
|
17
17
|
transform_and_deploy(path, options[:force]) unless File.directory?(path)
|
18
18
|
end
|
19
19
|
end
|
@@ -19,7 +19,7 @@ module Breeze
|
|
19
19
|
report 'MACHINE IMAGES',
|
20
20
|
['Name or Location', 'Image ID', 'Owner', 'Image Type', 'Public'],
|
21
21
|
fog.images.all('Owner' => Breeze::CONFIGURATION[:image_owner]).map{ |i|
|
22
|
-
[i.
|
22
|
+
[i.display_name, i.id, i.owner_id, i.full_type, i.is_public]
|
23
23
|
}
|
24
24
|
end
|
25
25
|
|
data/lib/breeze/tasks/server.rb
CHANGED
@@ -14,12 +14,14 @@ module Breeze
|
|
14
14
|
create_server(options)
|
15
15
|
end
|
16
16
|
|
17
|
-
desc 'destroy INSTANCE_ID', 'Terminate a running (or stopped) server instance'
|
17
|
+
desc 'destroy INSTANCE_ID [...]', 'Terminate a running (or stopped) server instance'
|
18
18
|
method_options :force => false
|
19
|
-
def destroy(
|
20
|
-
|
21
|
-
|
22
|
-
server.
|
19
|
+
def destroy(*instance_ids)
|
20
|
+
instance_ids.each do |instance_id|
|
21
|
+
server = fog.servers.get(instance_id)
|
22
|
+
if force_or_accept?("Terminate server #{server.display_name}?")
|
23
|
+
server.destroy
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
@@ -34,31 +36,45 @@ module Breeze
|
|
34
36
|
return server
|
35
37
|
end
|
36
38
|
|
37
|
-
|
39
|
+
# Can take a host name or an ip address. Resolves the host name
|
40
|
+
# and returns the ip address if get_ip is passed in as true.
|
41
|
+
def wait_until_host_is_available(host, get_ip=false)
|
38
42
|
if Resolv.getaddresses(host).empty?
|
39
43
|
print("Waiting for #{host} to resolve")
|
40
44
|
wait_until('ready!') { Resolv.getaddresses(host).any? }
|
41
45
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
46
|
+
host = Resolv.getaddresses(host).first if get_ip
|
47
|
+
unless remote_is_available?(host)
|
48
|
+
print("Waiting for #{host} to accept connections")
|
49
|
+
wait_until('ready!') { remote_is_available?(host) }
|
50
|
+
end
|
51
|
+
return host
|
45
52
|
end
|
46
53
|
|
47
54
|
def remote_is_available?(host)
|
48
|
-
execute(
|
55
|
+
execute("%{ssh_command} -q %{ssh_user}@%{host} exit", :host => host)
|
49
56
|
end
|
50
57
|
|
58
|
+
# Execute a command on the remote host. Args is a hash that must include :host.
|
51
59
|
def remote(command, args)
|
52
60
|
args[:command] = command
|
53
|
-
execute(
|
61
|
+
execute("%{ssh_command} %{ssh_user}@%{host} '%{command}'", args)
|
54
62
|
end
|
55
63
|
|
56
64
|
def upload(file_pattern, args)
|
57
65
|
args[:file_pattern] = file_pattern
|
58
|
-
|
66
|
+
args[:remote_path] ||= './'
|
67
|
+
execute('rsync -e "%{ssh_command}" -v %{file_pattern} %{ssh_user}@%{host}:%{remote_path}', args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def download(remote_path, args)
|
71
|
+
args[:remote_path] = remote_path
|
72
|
+
args[:local_path] ||= './'
|
73
|
+
execute('rsync -e "%{ssh_command}" -v %{ssh_user}@%{host}:%{remote_path} %{local_path}', args)
|
59
74
|
end
|
60
75
|
|
61
76
|
def execute(command, args)
|
77
|
+
args = CONFIGURATION[:ssh].merge(args)
|
62
78
|
command = CONFIGURATION[command] if command.is_a?(Symbol)
|
63
79
|
# system(command % args)
|
64
80
|
# rescue ArgumentError # for ruby 1.8 compatibility
|
@@ -28,5 +28,16 @@ module Breeze
|
|
28
28
|
puts("NOTICE: it may take a while before the new image shows up in describe:images")
|
29
29
|
end
|
30
30
|
|
31
|
+
desc 'destroy IMAGE_ID [...]', 'Deregister the image and destroy the related volume snapshot'
|
32
|
+
method_options :force => false
|
33
|
+
def destroy(*image_ids)
|
34
|
+
image_ids.each do |image_id|
|
35
|
+
image = fog.images.get(image_id)
|
36
|
+
if force_or_accept?("Destroy image #{image.display_name}?")
|
37
|
+
image.deregister(true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
31
42
|
end
|
32
43
|
end
|
data/lib/templates/Thorfile
CHANGED
@@ -13,12 +13,9 @@ Breeze::CONFIGURATION = {
|
|
13
13
|
:aws_secret_access_key => 'YOUR-SECTET-ACCESS-KEY'
|
14
14
|
},
|
15
15
|
|
16
|
-
#
|
17
|
-
# an application.
|
18
|
-
|
19
|
-
:remote_available? => 'ssh -q ubuntu@%{host} exit',
|
20
|
-
:remote_command => "ssh ubuntu@%{host} '%{command}'",
|
21
|
-
:upload_command => 'rsync -v %{file_pattern} ubuntu@%{host}:%{remote_path}',
|
16
|
+
# ssh_command and ssh_user are required in order to create a server image or deploy
|
17
|
+
# an application. Use "ssh -i /path/to/key" if not using your default ssh key.
|
18
|
+
:ssh => {:ssh_command => "ssh -o 'UserKnownHostsFile /dev/null'", :ssh_user => 'ubuntu'},
|
22
19
|
|
23
20
|
# :rollback_window specifies the number of minutes to keep old instances running after new ones
|
24
21
|
# have been deployed. Rollback is no longer possible when the old instances have been destroyed.
|
@@ -91,7 +88,7 @@ class Breeze::App
|
|
91
88
|
end
|
92
89
|
|
93
90
|
# Define staging:start etc. below. The constants are also needed by
|
94
|
-
# inherited tasks: deploy, rollback, enable and
|
91
|
+
# inherited tasks: deploy, rollback, enable, disable and ssh.
|
95
92
|
|
96
93
|
class Staging < Breeze::App
|
97
94
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 4
|
9
|
+
version: 0.0.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Markus Bengts
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-04-19 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -44,6 +44,32 @@ dependencies:
|
|
44
44
|
version: "0.7"
|
45
45
|
type: :runtime
|
46
46
|
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: cucumber
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id003
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: aruba
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
type: :development
|
72
|
+
version_requirements: *id004
|
47
73
|
description: |
|
48
74
|
Breeze makes it easy to automate server installation and configuration. It provides
|
49
75
|
example scripts and configuration files that you can modify and keep in your revision
|
@@ -65,6 +91,8 @@ files:
|
|
65
91
|
- Rakefile
|
66
92
|
- bin/breeze
|
67
93
|
- breeze.gemspec
|
94
|
+
- features/erb_conf.feature
|
95
|
+
- features/support/env.rb
|
68
96
|
- lib/breeze.rb
|
69
97
|
- lib/breeze/fog_extensions.rb
|
70
98
|
- lib/breeze/fog_extensions/aws.rb
|
@@ -128,6 +156,7 @@ rubyforge_project: breeze
|
|
128
156
|
rubygems_version: 1.3.7
|
129
157
|
signing_key:
|
130
158
|
specification_version: 3
|
131
|
-
summary: Thor tasks to manage
|
132
|
-
test_files:
|
133
|
-
|
159
|
+
summary: Thor tasks to manage cloud computing resources and deployments
|
160
|
+
test_files:
|
161
|
+
- features/erb_conf.feature
|
162
|
+
- features/support/env.rb
|