capify-ec2 1.3.7 → 1.4.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 1.4.0.pre1 (Feb 15, 2013)
2
+
3
+ Features:
4
+
5
+ - New rolling deployment mode, allows you to deploy to your instances in serial, rather than in parallel, with an option to perform a healthcheck before proceeding to the next instance. For more information on this feature, check out the [documentation](readme.md#rolling-deployments).
6
+ - The documentation has been rewritten to make it clearer how to use Capify-EC2 and what the available options are.
7
+
1
8
  ## 1.3.7 (Jan 20, 2013)
2
9
 
3
10
  - Make the behaviour for passing hash/filename consistent
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Forward
1
+ Copyright (c) 2011, 2012, 2013 Forward
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/capify-ec2.gemspec CHANGED
@@ -7,10 +7,10 @@ Gem::Specification.new do |s|
7
7
  s.version = Capify::Ec2::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Noah Cantor", "Siddharth Dawara"]
10
- s.email = ["noah.cantor@forward.co.uk", "siddharth.dawara@forward.co.uk"]
10
+ s.email = ["capify-ec2@forwardtechnology.co.uk"]
11
11
  s.homepage = "http://github.com/forward/capify-ec2"
12
- s.summary = %q{Grabs roles from ec2's tags and autogenerates capistrano tasks}
13
- s.description = %q{Grabs roles from ec2's tags and autogenerates capistrano tasks}
12
+ s.summary = %q{Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.}
13
+ s.description = %q{Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.}
14
14
 
15
15
  s.rubyforge_project = "capify-ec2"
16
16
 
data/lib/capify-ec2.rb CHANGED
@@ -57,7 +57,7 @@ class CapifyEc2
57
57
 
58
58
  # Title row.
59
59
  puts sprintf "%-3s %s %s %s %s %s %s %s",
60
- '',
60
+ 'Num' .bold,
61
61
  'Name' .ljust( column_widths[:name] ).bold,
62
62
  'ID' .ljust( 10 ).bold,
63
63
  'Type' .ljust( column_widths[:type] ).bold,
@@ -165,4 +165,48 @@ class CapifyEc2
165
165
  STDERR.puts "#{instance.name}: tests timed out after #{time_elapsed} seconds."
166
166
  end
167
167
  end
168
+
169
+ def instance_health_by_url(dns, port, path, expected_response, options = {})
170
+ protocol = options[:https] ? 'https://' : 'http://'
171
+ uri = URI("#{protocol}#{dns}:#{port}#{path}")
172
+ http = Net::HTTP.new(uri.host, uri.port)
173
+
174
+ result = nil
175
+
176
+ begin
177
+ Timeout::timeout(options[:timeout]) do
178
+ begin
179
+ result = http.get(uri.path)
180
+ raise "Server responded with '#{result.code}: #{result.body}', expected '#{expected_response}'" unless result.body == expected_response
181
+ rescue => e
182
+ puts "[Capify-EC2] Unexpected response: #{e}..."
183
+ sleep 1
184
+ retry
185
+ end
186
+ end
187
+ rescue Timeout::Error => e
188
+ end
189
+ result ? result.body == expected_response : false
190
+ end
191
+ end
192
+
193
+ def instance_dns_with_name_tag(dns)
194
+ name_tag = ''
195
+ current_node = capify_ec2.desired_instances.select { |instance| instance.dns_name == dns }
196
+ name_tag = current_node.first.tags['Name'] unless current_node.empty?
197
+ "#{dns} (#{name_tag})"
198
+ end
199
+
200
+ def format_rolling_deploy_results(all_servers, results)
201
+ puts '[Capify-EC2] None.' unless results.any?
202
+ results.each {|server| puts "[Capify-EC2] #{instance_dns_with_name_tag(server)} with #{all_servers[server].count >1 ? 'roles' : 'role'} '#{all_servers[server].join(', ')}'."}
203
+ end
204
+
205
+ class CapifyEC2RollingDeployError < Exception
206
+ attr_reader :dns
207
+
208
+ def initialize(msg, dns)
209
+ super(msg)
210
+ @dns = dns
211
+ end
168
212
  end
@@ -1,5 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), '../capify-ec2')
2
2
  require 'colored'
3
+ require 'pp'
3
4
 
4
5
  Capistrano::Configuration.instance(:must_exist).load do
5
6
  def capify_ec2
@@ -34,24 +35,121 @@ Capistrano::Configuration.instance(:must_exist).load do
34
35
  task :server_names do
35
36
  puts capify_ec2.server_names.sort
36
37
  end
37
-
38
+
38
39
  desc "Allows ssh to instance by id. cap ssh <INSTANCE NAME>"
39
40
  task :ssh do
40
41
  server = variables[:logger].instance_variable_get("@options")[:actions][1]
41
42
  instance = numeric?(server) ? capify_ec2.desired_instances[server.to_i] : capify_ec2.get_instance_by_name(server)
42
- port = ssh_options[:port] || 22
43
- command = "ssh -p #{port} #{user}@#{instance.contact_point}"
44
- puts "Running `#{command}`"
45
- exec(command)
43
+
44
+ if instance and instance.contact_point then
45
+ port = ssh_options[:port] || 22
46
+ command = "ssh -p #{port} #{user}@#{instance.contact_point}"
47
+ puts "Running `#{command}`"
48
+ exec(command)
49
+ else
50
+ puts "[Capify-EC2] Error: You did not specify the instance number, which can be found via the 'ec2:status' command as follows:".bold.red
51
+ top.ec2.status
52
+ end
46
53
  end
47
54
  end
48
-
55
+
49
56
  namespace :deploy do
50
57
  before "deploy", "ec2:deregister_instance"
51
58
  after "deploy", "ec2:register_instance"
52
59
  after "deploy:rollback", "ec2:register_instance"
53
60
  end
54
61
 
62
+ desc "Deploy to servers one at a time."
63
+ task :rolling_deploy do
64
+ puts "[Capify-EC2] Performing rolling deployment..."
65
+
66
+ all_servers = {}
67
+ all_roles = {}
68
+
69
+ roles.each do |role|
70
+ role[1].servers.each do |s|
71
+ all_servers[ s.host.to_s ] ||= []
72
+ all_servers[ s.host.to_s ] << role[0]
73
+ all_roles[ role[0] ] = {:options => {:healthcheck => s.options[:healthcheck] ||= nil}} unless all_roles[ role[0] ]
74
+ end
75
+ end
76
+
77
+ successful_deploys = []
78
+ failed_deploys = []
79
+
80
+ begin
81
+ all_servers.each do |server_dns,server_roles|
82
+
83
+ roles.clear
84
+
85
+ server_roles.each do |a_role|
86
+ #TODO: Look at defining any options associated to the specific role, maybe through calling 'ec2_role'.
87
+ role a_role, server_dns
88
+ end
89
+
90
+ puts "[Capify-EC2]"
91
+ puts "[Capify-EC2] Beginning deployment to #{instance_dns_with_name_tag(server_dns)} with #{server_roles.count > 1 ? 'roles' : 'role'} '#{server_roles.join(', ')}'...".bold
92
+
93
+ # Call the standard 'cap deploy' task with our redefined role containing a single server.
94
+ top.deploy.default
95
+
96
+ server_roles.each do |a_role|
97
+
98
+ # If a healthcheck is defined for this role, run it.
99
+ if all_roles[a_role][:options][:healthcheck]
100
+ options = {}
101
+ options[:https] = all_roles[a_role][:options][:healthcheck][:https] ||= false
102
+ options[:timeout] = all_roles[a_role][:options][:healthcheck][:timeout] ||= 60
103
+ puts "[Capify-EC2] Starting healthcheck for role '#{a_role}'..."
104
+ healthcheck = capify_ec2.instance_health_by_url( server_dns,
105
+ all_roles[a_role][:options][:healthcheck][:port],
106
+ all_roles[a_role][:options][:healthcheck][:path],
107
+ all_roles[a_role][:options][:healthcheck][:result],
108
+ options )
109
+ if healthcheck
110
+ puts "[Capify-EC2] Healthcheck for role '#{a_role}' successful.".green.bold
111
+ else
112
+ puts "[Capify-EC2] Healthcheck for role '#{a_role}' failed!".red.bold
113
+ raise CapifyEC2RollingDeployError.new("Healthcheck timeout exceeded", server_dns)
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ puts "[Capify-EC2] Deployment successful to #{instance_dns_with_name_tag(server_dns)}.".green.bold
120
+ successful_deploys << server_dns
121
+
122
+ end
123
+ rescue CapifyEC2RollingDeployError => e
124
+ failed_deploys << e.dns
125
+ puts "[Capify-EC2]"
126
+ puts "[Capify-EC2] Deployment aborted due to error: #{e}!".red.bold
127
+
128
+ rescue => e
129
+ failed_deploys << roles.values.first.servers.first.host
130
+ puts "[Capify-EC2]"
131
+ puts "[Capify-EC2] Deployment aborted due to error: #{e}!".red.bold
132
+
133
+ else
134
+ puts "[Capify-EC2]"
135
+ puts "[Capify-EC2] Rolling deployment completed.".green.bold
136
+
137
+ end
138
+
139
+ puts "[Capify-EC2]"
140
+ puts "[Capify-EC2] Successful servers:"
141
+ format_rolling_deploy_results( all_servers, successful_deploys )
142
+
143
+ puts "[Capify-EC2]"
144
+ puts "[Capify-EC2] Failed servers:"
145
+ format_rolling_deploy_results( all_servers, failed_deploys )
146
+
147
+ puts "[Capify-EC2]"
148
+ puts "[Capify-EC2] Pending servers:"
149
+ pending_deploys = (all_servers.keys - successful_deploys) - failed_deploys
150
+ format_rolling_deploy_results( all_servers, pending_deploys )
151
+ end
152
+
55
153
  def ec2_roles(*roles)
56
154
  server_name = variables[:logger].instance_variable_get("@options")[:actions].first unless variables[:logger].instance_variable_get("@options")[:actions][1].nil?
57
155
 
@@ -130,16 +228,19 @@ Capistrano::Configuration.instance(:must_exist).load do
130
228
  def define_role(role, instance)
131
229
  options = role[:options] || {}
132
230
  variables = role[:variables] || {}
133
-
231
+
134
232
  cap_options = options.inject({}) do |cap_options, (key, value)|
135
233
  cap_options[key] = true if value.to_s == instance.name
136
234
  cap_options
137
235
  end
138
-
236
+
139
237
  ec2_options = instance.tags["Options"] || ""
140
238
  ec2_options.split(%r{,\s*}).compact.each { |ec2_option| cap_options[ec2_option.to_sym] = true }
141
-
142
- variables.each { |key, value| set key, value }
239
+
240
+ variables.each do |key, value|
241
+ set key, value
242
+ cap_options[key] = value unless cap_options.has_key? key
243
+ end
143
244
 
144
245
  role role[:name].to_sym, instance.contact_point, cap_options
145
246
  end
@@ -1,6 +1,6 @@
1
1
  module Capify
2
2
  module Ec2
3
- VERSION = "1.3.7"
3
+ VERSION = "1.4.0.pre1"
4
4
  end
5
5
  end
6
6
 
data/readme.md CHANGED
@@ -1,194 +1,442 @@
1
- Capify Ec2
2
- ====================================================
1
+ ## Capify-EC2
3
2
 
4
- capify-ec2 is used to generate capistrano namespaces using ec2 tags.
3
+ Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.
5
4
 
6
- eg: If you have three servers on amazon's ec2.
7
5
 
8
- server-1 Tag: Roles => "web", Options => "cron,resque, db"
9
- server-2 Tag: Roles => "db"
10
- server-3 Tag: Roles => "web,db, app"
11
-
12
- Installing
6
+ ### Installation
13
7
 
14
8
  gem install capify-ec2
15
9
 
16
- In your deploy.rb:
10
+ or add the gem to your project's Gemfile.
11
+
12
+ You will need to create a YML configuration file at 'config/ec2.yml' that looks like the following:
13
+
14
+ ```ruby
15
+ :aws_access_key_id: "YOUR ACCESS KEY"
16
+ :aws_secret_access_key: "YOUR SECRET"
17
+ :aws_params:
18
+ :region: 'eu-west-1'
19
+ :load_balanced: true
20
+ :project_tag: "YOUR APP NAME"
21
+ ```
22
+
23
+ Finally, add the gem to your 'deploy.rb':
17
24
 
18
25
  ```ruby
19
26
  require "capify-ec2/capistrano"
27
+ ```
28
+
29
+
30
+
31
+ #### Configuration
32
+
33
+ Note: 'aws_access_key_id', 'aws_secret_access_key', and 'region' are required. Other settings are optional.
34
+
35
+ * :project_tag
36
+
37
+ If this is defined, Capify-EC2 will only create namespaces and tasks for the EC2 instances that have a matching 'Project' tag. By default, all instances available to the configured AWS access key will be used.
38
+
39
+ It is possible to include multiple projects simultaneously by using the :project_tags parameter, like so:
40
+
41
+ ```ruby
42
+ :project_tags:
43
+ - "YOUR APP NAME"
44
+ - "YOUR OTHER APP NAME"
45
+ ```
46
+
47
+ * :load_balanced
48
+
49
+ When ':load_balanced' is set to 'true', Capify-EC2 uses pre and post-deploy hooks to deregister the instance from an associated Elastic Load Balancer, perform the actual deploy, then finally reregister with the ELB and validated the instance health.
50
+ Note: This options only applies to deployments made to an individual instance, using the command 'cap INSTANCE_NAME_HERE deploy' - it doesn't apply to roles.
51
+
52
+
53
+
54
+ #### EC2 Tags
55
+
56
+ You will need to create instance tags using the AWS Management Console or API, to further configure Capify-EC2. The following tags are used:
57
+
58
+ * Tag 'Project'
59
+
60
+ Used with the ':project_tag' option in 'config/ec2.yml' to limit Capify-EC2's functionality to a subset of your instances.
61
+
62
+ * Tag 'Roles'
63
+
64
+ A comma seperated list of roles that will be converted into Capistrano namespaces, for example 'app,workers', 'app,varnish,workers', 'db' and so on.
65
+
66
+ * Tag 'Options'
67
+
68
+ A comma seperated list of options which will be defined as 'true' for that instance. See the 'Options' section below for more information on their use.
69
+ task :bob, :only => {:option_nmame => true}
70
+ on_no_matching_servers => :continue
71
+ "one of those ten is cron" etc.
72
+
73
+
74
+
75
+ ### Usage
76
+
77
+ In our examples, imagine that you have three servers on EC2 defined as follows:
78
+
79
+ server-1 Tags: Name => "server-1", Roles => "web", Options => "cron,resque"
80
+ server-2 Tags: Name => "server-2", Roles => "db"
81
+ server-3 Tags: Name => "server-3", Roles => "web,db,app"
82
+
83
+ #### Single Roles
84
+
85
+ You need to add a call to 'ec2_roles' in your 'deploy.rb', like so:
86
+
87
+ ```ruby
20
88
  ec2_roles :web
21
89
  ```
22
90
 
23
- Will generate
91
+ This will generate the following tasks:
24
92
 
25
93
  ```ruby
26
94
  task :server-1 do
27
- role :web, {server-1 public dns fetched from Amazon}, :cron=>true, :resque=>true
95
+ role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
28
96
  end
29
97
 
30
98
  task :server-3 do
31
- role :web, {server-1 public dns fetched from Amazon}
99
+ role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
32
100
  end
33
101
 
34
102
  task :web do
35
- role :web, {server-1 public dns fetched from Amazon}, :cron=>true, :resque=>true
36
- role :web, {server-3 public dns fetched from Amazon}
103
+ role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
104
+ role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
37
105
  end
38
106
  ```
39
107
 
40
- Additionally
108
+ Note that there are no tasks created for 'server-2', as it does not have the role 'web'. If we were to change the 'ec2_roles' definition in your 'deploy.rb' to the following:
41
109
 
42
110
  ```ruby
43
- require "capify-ec2/capistrano"
44
111
  ec2_roles :db
45
112
  ```
46
113
 
47
- Will generate
114
+ Then we will instead see the following tasks generated:
48
115
 
49
116
  ```ruby
50
117
  task :server-2 do
51
- role :db, {server-2 public dns fetched from Amazon}
118
+ role :db, SERVER-2_EC2_PUBLIC_DNS_HERE
52
119
  end
53
120
 
54
121
  task :server-3 do
55
- role :db, {server-3 public dns fetched from Amazon}
122
+ role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
56
123
  end
57
124
 
58
125
  task :db do
59
- role :db, {server-2 public dns fetched from Amazon}
60
- role :db, {server-3 public dns fetched from Amazon}
126
+ role :db, SERVER-2_EC2_PUBLIC_DNS_HERE
127
+ role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
61
128
  end
62
129
  ```
63
130
 
64
- Running
131
+
132
+ #### Multiple Roles
133
+
134
+ If you want to create tasks for servers using multiple roles, you can call 'ec2_roles' multiple times in your 'deploy.rb' as follows:
65
135
 
66
136
  ```ruby
67
- cap web ec2:date
137
+ ec2_roles :web
138
+ ec2_roles :db
68
139
  ```
69
140
 
70
- will run the date command on all server's tagged with the web role
141
+ Which would generate the following tasks:
71
142
 
72
- Running
143
+ ```ruby
144
+ task :server-1 do
145
+ role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
146
+ end
147
+
148
+ task :server-2 do
149
+ role :db, SERVER-2_EC2_PUBLIC_DNS_HERE
150
+ end
151
+
152
+ task :server-3 do
153
+ role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
154
+ role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
155
+ end
156
+
157
+ task :web do
158
+ role :web, SERVER-1_EC2_PUBLIC_DNS_HERE
159
+ role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
160
+ end
161
+
162
+ task :db do
163
+ role :db, SERVER-2_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
164
+ role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
165
+ end
166
+ ```
167
+
168
+
169
+
170
+ #### Role Variables
171
+
172
+ You can define custom variables which will be set as standard Capistrano variables within the scope of the role you define them one, for example:
173
+
174
+ ```ruby
175
+ ec2_roles {:name=>"web", :variables => {:rails_env => 'staging'}}
176
+ ```
177
+
178
+ In this case, instances that that are tagged with the 'web' role will have the custom variable 'rails_env' available to them in any tasks they use. The following tasks would be generated:
73
179
 
74
180
  ```ruby
75
- cap server-1 ec2:register_instance -s loadbalancer=elb-1
181
+ task :server-1 do
182
+ role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true, :rails_env=>'staging'
183
+ end
184
+
185
+ task :server-3 do
186
+ role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
187
+ end
188
+
189
+ task :web do
190
+ role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true, :rails_env=>'staging'
191
+ role :web, SERVER-3_EC2_PUBLIC_DNS_HERE, :rails_env=>'staging'
192
+ end
76
193
  ```
77
194
 
78
- will register server-1 to be used by elb-1
79
195
 
80
- Running
196
+
197
+ #### Options
198
+
199
+ ##### Via EC2 Tags
200
+
201
+ As mentioned in the 'EC2 Tags' section, creating an 'Options' tag on your EC2 instances will define those options as 'true' for the associated instance. This allows you to refine your capistrano tasks.
202
+ For example, if we had the following group of instances in EC2:
203
+
204
+ server-A Tags: Name => "server-A", Roles => "web"
205
+ server-B Tags: Name => "server-B", Roles => "web"
206
+ server-C Tags: Name => "server-C", Roles => "web", Options => "worker"
207
+
208
+ You could then create a task in your 'deploy.rb' that will only be executed on the worker machine, like so:
81
209
 
82
210
  ```ruby
83
- cap server-1 ec2:deregister_instance
211
+ task :reload_workers => :web, :only=>{:worker} do
212
+ # Do something to a server with cron on it
213
+ end
84
214
  ```
85
215
 
86
- will remove server-1 from whatever instance it is currently
87
- registered against.
216
+ ##### Via Role Definitions
88
217
 
89
- Running
218
+ As well as defining Options at an instance level via EC2 tags, you can define an Option in your 'deploy.rb' at the same time as defining the role, as follows:
90
219
 
91
220
  ```ruby
92
- cap ec2:status
221
+ ec2_roles {:name=>"web", :options=>{:worker=>"server-C"}}
93
222
  ```
94
223
 
95
- will list the currently running servers and their associated details
96
- (public dns, instance id, roles etc)
224
+ In this case, you set the value of ':worker' equal to the instance name you want to be a worker.
225
+
97
226
 
98
- Running
227
+
228
+ #### Deploying
229
+
230
+ Once you have defined the various roles used by your application, you can deploy to it as you normally would a namespace, for example if you define the following in your 'deploy.rb':
99
231
 
100
232
  ```ruby
101
- cap ec2:ssh #
233
+ ec2_roles :web
234
+ ec2_roles :app
102
235
  ```
103
236
 
104
- will launch ssh using the user and port specified in your configuration.
105
- The # argument is the index of the server to ssh into. Use the 'ec2:status'
106
- command to see the list of servers with their indices.
237
+ You can deploy to just the 'web' instances like so:
107
238
 
108
- More options
109
- ====================================================
239
+ ```ruby
240
+ cap web deploy
241
+ ```
110
242
 
111
- In addition to specifying options (e.g. 'cron') at the server level, it is also possible to specify it at the project level.
112
- Use with caution! This does not work with autoscaling.
243
+ If you've defined multiple roles, you can deploy to them all by chaining the tasks, like so:
113
244
 
114
245
  ```ruby
115
- ec2_roles {:name=>"web", :options=>{:cron=>"server-1"}}
246
+ cap web app deploy
116
247
  ```
117
248
 
118
- Will generate
249
+
250
+
251
+ ##### Default Deploys
252
+
253
+ You can set a role as the default so that it will be included when you run 'cap deploy' without specifying any roles, for example in your 'deploy.rb':
119
254
 
120
255
  ```ruby
121
- task :server-1 do
122
- role :web, {server-1 public dns fetched from Amazon}, :cron=>true
123
- end
256
+ ec2_roles {:name=>"web", :options{:default=>true}
257
+ ```
124
258
 
125
- task :server-3 do
126
- role :web, {server-1 public dns fetched from Amazon}
127
- end
259
+ Then run:
128
260
 
129
- task :web do
130
- role :web, {server-1 public dns fetched from Amazon}, :cron=>true
131
- role :web, {server-3 public dns fetched from Amazon}
132
- end
261
+ ```ruby
262
+ cap deploy
133
263
  ```
134
264
 
135
- Which is cool if you want a task like this in deploy.rb
265
+ You can set multiple roles as defaults, so they are all included when you run 'cap deploy', like so:
136
266
 
137
267
  ```ruby
138
- task :update_cron => :web, :only=>{:cron} do
139
- Do something to a server with cron on it
140
- end
268
+ ec2_roles {:name=>"web", :options{:default=>true}
269
+ ec2_roles {:name=>"db", :options{:default=>true}
270
+ ```
271
+
272
+
273
+
274
+ #### Rolling Deployments
275
+
276
+ This feature allows you to deploy your code to instances one at a time, rather than simultaneously. This becomes useful for more complex applications that may take longer to startup after a deployment. Capistrano will perform a full deploy (including any custom hooks) against a single instance, optionally perform a HTTP healthcheck against the instance, then proceed to the next instance if deployment was successful.
141
277
 
142
- ec2_roles :name=>:web, :options=>{ :default => true }
278
+ ##### Usage
279
+
280
+ To use the rolling deployment feature without a healthcheck, simple run your deployments with the following command:
281
+
282
+ ```ruby
283
+ cap rolling_deploy
143
284
  ```
144
285
 
145
- Will make :web the default role so you can just type 'cap deploy'.
146
- Multiple roles can be defaults so:
286
+ You can restrict the scope of the rolling deploy by targetting one or more roles like so:
147
287
 
148
288
  ```ruby
149
- ec2_roles :name=>:web, :options=>{ :default => true }
150
- ec2_roles :name=>:app, :options=>{ :default => true }
289
+ cap web rolling_deploy
151
290
  ```
152
291
 
153
- would be the equivalent of 'cap app web deploy'
292
+ ```ruby
293
+ cap web db rolling_deploy
294
+ ```
154
295
 
155
- Ec2 config
156
- ====================================================
296
+ ##### Usage with Healthchecks
157
297
 
158
- This gem requires 'config/ec2.yml' in your project.
159
- The yml file needs to look something like this:
298
+ When defining a role with the 'ec2_role' command, if you configure a healthcheck for that role as follows, it will automatically be used during the rolling deployment:
160
299
 
161
300
  ```ruby
162
- :aws_access_key_id: "YOUR ACCESS KEY"
163
- :aws_secret_access_key: "YOUR SECRET"
164
- :aws_params:
165
- :region: 'eu-west-1'
166
- :load_balanced: true
167
- :project_tag: "YOUR APP NAME"
301
+ ec2_roles { :name => "web",
302
+ :variables => {
303
+ :healthcheck => {
304
+ :path => '/status',
305
+ :port => 80,
306
+ :result => 'OK'
307
+ }
308
+ }
168
309
  ```
169
- aws_access_key_id, aws_secret_access_key, and region are required. Other settings are optional.
170
310
 
171
- If :load_balanced is set to true, the gem uses pre and post-deploy
172
- hooks to deregister the instance, reregister it, and validate its
173
- health.
174
- :load_balanced only works for individual instances, not for roles.
311
+ In this example, the following URL would be generated:
175
312
 
176
- The :project_tag parameter is optional. It will limit any commands to
177
- running against those instances with a "Project" tag set to the value
178
- "YOUR APP NAME". It is also possible to apply commands to multiple projects using the :project_tags parameter, like so:
313
+ ```
314
+ http://EC2_INSTANCE_PUBLIC_DNS_HERE:80/status
315
+ ```
316
+
317
+ And the contents of the page at that URL must match 'OK' for the healthcheck to pass. If unsuccessful, the healthcheck is repeated every second, until a timeout of 60 seconds is reached, at which point the rolling deployment is aborted, and a progress summary displayed.
318
+
319
+ The default timeout is 60 seconds, which can be overridden by setting ':timeout' to a custom value in seconds. The protocol used defaults to 'http://', however you can switch to 'https://' by setting ':https' equal to 'true'. For example:
320
+
321
+ ```ruby
322
+ ec2_roles { :name => "web",
323
+ :variables => {
324
+ :healthcheck => {
325
+ :path => '/status',
326
+ :port => 80,
327
+ :result => 'OK',
328
+ :https => true,
329
+ :timeout => 10
330
+ }
331
+ }
332
+ ```
333
+
334
+ Sets a 10 second timeout, and performs the health check over HTTPS.
335
+
336
+ #### Managing Load Balancers
337
+
338
+ You can use the following commands to deregister and reregister instances in an Elastic Load Balancer.
339
+
340
+ ```ruby
341
+ cap SERVER_NAME_HERE ec2:deregister_instance
342
+ ```
179
343
 
180
344
  ```ruby
181
- :project_tags:
182
- - "YOUR APP NAME"
183
- - "YOUR OTHER APP NAME"
345
+ cap SERVER_NAME_HERE ec2:register_instance -s loadbalancer=ELB_NAME_HERE
184
346
  ```
185
347
 
186
- ## Development
348
+ You need to specify the ELB when reregistering an instance, but not when deregistering. This can also be done automatically using the ':load_balanced' setting (see the 'Configuration' section above).
349
+
350
+
351
+
352
+ #### Viewing All Instances
353
+
354
+ The following command will generate a listing of all instances that match your configuration (projects and roles), along with their associated details:
355
+
356
+ ```ruby
357
+ cap ec2:status
358
+ ```
359
+
360
+
361
+
362
+ #### Connecting to an Instance via SSH
363
+
364
+ Using the 'cap ec2:ssh' command, you can quickly connect to a specific instance, by checking the listing from 'ec2:status' and using the instance number as a parameter, for example:
365
+
366
+ ```ruby
367
+ cap ec2:ssh 1
368
+ ```
369
+
370
+ will attempt to connect to instance number 1 (as shown in 'ec2:status'), using the public DNS address provided by AWS.
371
+
372
+
373
+
374
+ #### Other Commands
375
+
376
+ Running the following command:
377
+
378
+ ```ruby
379
+ cap ec2:date
380
+ ```
381
+
382
+ Will execute the 'date' command on all instances that match your configuration (projects and roles). You can limit this further by using a role, for example:
383
+
384
+ ```ruby
385
+ cap web ec2:date
386
+ ```
387
+
388
+ Will restrict the 'date' command so it is only run on instances that are tagged with the 'web' role. You can chain many roles together to increase the scope of the command:
389
+
390
+ ```ruby
391
+ cap web db ec2:date
392
+ ```
393
+
394
+ ##### Cap Invoke
395
+
396
+ You can use the standard Capistrano 'invoke' task to run an arbitrary command on your instances, for example:
397
+
398
+ ```ruby
399
+ cap COMMAND='uptime' invoke
400
+ ```
401
+
402
+ Will run the 'uptime' command on all instances that match your configuration (projects and roles). As with the 'ec2:date' command, you can further limit this by using a role, like so:
403
+
404
+ ```ruby
405
+ cap web COMMAND='uptime' invoke
406
+ ```
407
+
408
+ You can also chain many roles together to increase the scope of the command:
409
+
410
+ ```ruby
411
+ cap web db COMMAND='uptime' invoke
412
+ ```
413
+
414
+ ##### Cap Shell
415
+
416
+ You can use the standard Capistrano 'shell' task to open an interactive terminal session with your instances, for example:
417
+
418
+ ```ruby
419
+ cap shell
420
+ ```
421
+
422
+ Will open an interactive terminal on all instances that match your configuration (projects and roles). You can of course limit the scope of the shell to a certain role or roles like so:
423
+
424
+ ```ruby
425
+ cap web shell
426
+ ```
427
+
428
+ ```ruby
429
+ cap web db shell
430
+ ```
431
+
432
+
433
+
434
+ ### Development
187
435
 
188
436
  Source hosted at [GitHub](http://github.com/forward/capify-ec2).
189
437
  Report Issues/Feature requests on [GitHub Issues](http://github.com/forward/capify-ec2/issues).
190
438
 
191
- ### Note on Patches/Pull Requests
439
+ #### Note on Patches/Pull Requests
192
440
 
193
441
  * Fork the project.
194
442
  * Make your feature addition or bug fix.
@@ -198,6 +446,6 @@ Report Issues/Feature requests on [GitHub Issues](http://github.com/forward/capi
198
446
  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
199
447
  * Send me a pull request. Bonus points for topic branches.
200
448
 
201
- ## Copyright
449
+ ### Copyright
202
450
 
203
- Copyright (c) 2012 Forward. See [LICENSE](https://github.com/forward/capify-ec2/blob/master/LICENSE) for details.
451
+ Copyright (c) 2011, 2012, 2013 Forward. See [LICENSE](https://github.com/forward/capify-ec2/blob/master/LICENSE) for details.
metadata CHANGED
@@ -1,73 +1,80 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: capify-ec2
3
- version: !ruby/object:Gem::Version
4
- version: 1.3.7
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ hash: -2106362774
5
+ prerelease: 6
6
+ segments:
7
+ - 1
8
+ - 4
9
+ - 0
10
+ - pre
11
+ - 1
12
+ version: 1.4.0.pre1
6
13
  platform: ruby
7
- authors:
14
+ authors:
8
15
  - Noah Cantor
9
16
  - Siddharth Dawara
10
17
  autorequire:
11
18
  bindir: bin
12
19
  cert_chain: []
13
- date: 2013-01-20 00:00:00.000000000 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
20
+
21
+ date: 2013-02-15 00:00:00 Z
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
16
24
  name: fog
17
- requirement: !ruby/object:Gem::Requirement
18
- none: false
19
- requirements:
20
- - - '='
21
- - !ruby/object:Gem::Version
22
- version: 1.3.1
23
- type: :runtime
24
25
  prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
+ requirement: &id001 !ruby/object:Gem::Requirement
26
27
  none: false
27
- requirements:
28
- - - '='
29
- - !ruby/object:Gem::Version
28
+ requirements:
29
+ - - "="
30
+ - !ruby/object:Gem::Version
31
+ hash: 25
32
+ segments:
33
+ - 1
34
+ - 3
35
+ - 1
30
36
  version: 1.3.1
31
- - !ruby/object:Gem::Dependency
32
- name: colored
33
- requirement: !ruby/object:Gem::Requirement
34
- none: false
35
- requirements:
36
- - - '='
37
- - !ruby/object:Gem::Version
38
- version: '1.2'
39
37
  type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: colored
40
41
  prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - '='
45
- - !ruby/object:Gem::Version
46
- version: '1.2'
47
- - !ruby/object:Gem::Dependency
48
- name: capistrano
49
- requirement: !ruby/object:Gem::Requirement
42
+ requirement: &id002 !ruby/object:Gem::Requirement
50
43
  none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
44
+ requirements:
45
+ - - "="
46
+ - !ruby/object:Gem::Version
47
+ hash: 11
48
+ segments:
49
+ - 1
50
+ - 2
51
+ version: "1.2"
55
52
  type: :runtime
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: capistrano
56
56
  prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
58
  none: false
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
62
- version: '0'
63
- description: Grabs roles from ec2's tags and autogenerates capistrano tasks
64
- email:
65
- - noah.cantor@forward.co.uk
66
- - siddharth.dawara@forward.co.uk
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ description: Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.
69
+ email:
70
+ - capify-ec2@forwardtechnology.co.uk
67
71
  executables: []
72
+
68
73
  extensions: []
74
+
69
75
  extra_rdoc_files: []
70
- files:
76
+
77
+ files:
71
78
  - .gitignore
72
79
  - Changelog.md
73
80
  - Gemfile
@@ -81,26 +88,38 @@ files:
81
88
  - readme.md
82
89
  homepage: http://github.com/forward/capify-ec2
83
90
  licenses: []
91
+
84
92
  post_install_message:
85
93
  rdoc_options: []
86
- require_paths:
94
+
95
+ require_paths:
87
96
  - lib
88
- required_ruby_version: !ruby/object:Gem::Requirement
97
+ required_ruby_version: !ruby/object:Gem::Requirement
89
98
  none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
- required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
107
  none: false
96
- requirements:
97
- - - ! '>='
98
- - !ruby/object:Gem::Version
99
- version: '0'
108
+ requirements:
109
+ - - ">"
110
+ - !ruby/object:Gem::Version
111
+ hash: 25
112
+ segments:
113
+ - 1
114
+ - 3
115
+ - 1
116
+ version: 1.3.1
100
117
  requirements: []
118
+
101
119
  rubyforge_project: capify-ec2
102
- rubygems_version: 1.8.23
120
+ rubygems_version: 1.8.15
103
121
  signing_key:
104
122
  specification_version: 3
105
- summary: Grabs roles from ec2's tags and autogenerates capistrano tasks
123
+ summary: Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.
106
124
  test_files: []
125
+