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 CHANGED
@@ -4,3 +4,4 @@ pkg/*
4
4
  .bundle
5
5
  Gemfile.lock
6
6
  /Thorfile
7
+ tmp/
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 AWS cloud computing resorces and deployments}
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
@@ -1,6 +1,6 @@
1
1
  module Breeze
2
2
 
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  autoload :Veur, 'breeze/veur'
5
5
 
6
6
  end
@@ -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
@@ -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
- thor("server:address:create #{server.id}") if options[:elastic_ip]
21
- thor("dns:record:create #{zone_id(public_server_name)} #{public_server_name}. A #{ip(server)} #{options[:dns_ttl]}")
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['config/breeze/configs/**/*'].each do |path|
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.name||i.location, i.id, i.owner_id, i.full_type, i.is_public]
22
+ [i.display_name, i.id, i.owner_id, i.full_type, i.is_public]
23
23
  }
24
24
  end
25
25
 
@@ -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(instance_id)
20
- server = fog.servers.get(instance_id)
21
- if force_or_accept?("Terminate server #{server.display_name}?")
22
- server.destroy
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
- def wait_until_host_is_available(host)
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
- return true if remote_is_available?(host)
43
- print("Waiting for #{host} to accept connections")
44
- wait_until('ready!') { remote_is_available?(host) }
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(:remote_available?, :host => host)
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(:remote_command, args)
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
- execute(:upload_command, args)
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
@@ -13,12 +13,9 @@ Breeze::CONFIGURATION = {
13
13
  :aws_secret_access_key => 'YOUR-SECTET-ACCESS-KEY'
14
14
  },
15
15
 
16
- # Remote and upload commands are required in order to create a server image or deploy
17
- # an application. Arguments marked with %{} are automatically replaced when the
18
- # commands are used. Use "ssh -i /path/to/key" if not using your default ssh key.
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 disable.
91
+ # inherited tasks: deploy, rollback, enable, disable and ssh.
95
92
 
96
93
  class Staging < Breeze::App
97
94
 
@@ -3,7 +3,7 @@
3
3
  @owner = 'rails'
4
4
  @group = 'rails'
5
5
  @perms = 0640
6
- @post = 'rake db:migrate RAILS_ENV=production'
6
+ @post = "sudo -u #{@owner} rake db:migrate RAILS_ENV=production"
7
7
 
8
8
  conf = CONFIGURATION[:default_db_options]
9
9
  %>
@@ -21,4 +21,5 @@ extract_and_install $IMAGE_MAGICK_PACKAGE "$IMAGE_MAGICK_OPTIONS"
21
21
  cat >> $HOME/.profile <<END_PROFILE
22
22
  # export RAILS_ENV=production
23
23
  # export EDITOR=vi
24
+ # alias rails='sudo -E -u rails ./script/rails'
24
25
  END_PROFILE
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 3
9
- version: 0.0.3
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-03-23 00:00:00 +01:00
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 AWS cloud computing resorces and deployments
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