eb_deployer_updated 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +127 -0
- data/Gemfile +10 -0
- data/LICENSE +22 -0
- data/README.md +136 -0
- data/Rakefile +12 -0
- data/TODOS.md +11 -0
- data/bin/eb_deploy +13 -0
- data/eb_deployer.gemspec +22 -0
- data/lib/eb_deployer/application.rb +96 -0
- data/lib/eb_deployer/aws_driver/beanstalk.rb +158 -0
- data/lib/eb_deployer/aws_driver/cloud_formation_driver.rb +53 -0
- data/lib/eb_deployer/aws_driver/s3_driver.rb +35 -0
- data/lib/eb_deployer/aws_driver.rb +8 -0
- data/lib/eb_deployer/cf_event_source.rb +26 -0
- data/lib/eb_deployer/cloud_formation_provisioner.rb +120 -0
- data/lib/eb_deployer/component.rb +45 -0
- data/lib/eb_deployer/config_loader.rb +64 -0
- data/lib/eb_deployer/default_component.rb +32 -0
- data/lib/eb_deployer/default_config.rb +20 -0
- data/lib/eb_deployer/default_config.yml +159 -0
- data/lib/eb_deployer/deployment_strategy/blue_green.rb +79 -0
- data/lib/eb_deployer/deployment_strategy/blue_only.rb +45 -0
- data/lib/eb_deployer/deployment_strategy/inplace_update.rb +16 -0
- data/lib/eb_deployer/deployment_strategy.rb +20 -0
- data/lib/eb_deployer/eb_environment.rb +204 -0
- data/lib/eb_deployer/eb_event_source.rb +35 -0
- data/lib/eb_deployer/environment.rb +60 -0
- data/lib/eb_deployer/event_poller.rb +51 -0
- data/lib/eb_deployer/package.rb +39 -0
- data/lib/eb_deployer/resource_stacks.rb +20 -0
- data/lib/eb_deployer/smoke_test.rb +23 -0
- data/lib/eb_deployer/tasks.rb +45 -0
- data/lib/eb_deployer/throttling_handling.rb +17 -0
- data/lib/eb_deployer/utils.rb +33 -0
- data/lib/eb_deployer/version.rb +3 -0
- data/lib/eb_deployer/version_cleaner.rb +30 -0
- data/lib/eb_deployer.rb +339 -0
- data/lib/generators/eb_deployer/install/install_generator.rb +82 -0
- data/lib/generators/eb_deployer/install/templates/eb_deployer.rake +1 -0
- data/lib/generators/eb_deployer/install/templates/eb_deployer.yml.erb +181 -0
- data/lib/generators/eb_deployer/install/templates/ebextensions/01_postgres_packages.config +5 -0
- data/lib/generators/eb_deployer/install/templates/postgres_rds.json +88 -0
- data/test/aws_driver_stubs.rb +350 -0
- data/test/beanstalk_test.rb +23 -0
- data/test/blue_green_deploy_test.rb +114 -0
- data/test/blue_only_deploy_test.rb +78 -0
- data/test/cf_event_poller_test.rb +32 -0
- data/test/cloud_formation_provisioner_test.rb +47 -0
- data/test/config_loader_test.rb +205 -0
- data/test/deploy_test.rb +42 -0
- data/test/eb_environment_test.rb +120 -0
- data/test/eb_event_poller_test.rb +32 -0
- data/test/inplace_update_deploy_test.rb +110 -0
- data/test/multi_components_deploy_test.rb +164 -0
- data/test/rails_generators_test.rb +67 -0
- data/test/resources_deploy_test.rb +191 -0
- data/test/smoke_test_test.rb +23 -0
- data/test/template_deploy_test.rb +13 -0
- data/test/test_helper.rb +68 -0
- data/test/tier_setting_deploy_test.rb +24 -0
- data/test/versions_deploy_test.rb +120 -0
- metadata +194 -0
data/lib/eb_deployer.rb
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'set'
|
|
3
|
+
require 'time'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
require 'aws-sdk-s3'
|
|
7
|
+
require 'aws-sdk-elasticbeanstalk'
|
|
8
|
+
require 'aws-sdk-cloudformation'
|
|
9
|
+
require 'optparse'
|
|
10
|
+
require 'erb'
|
|
11
|
+
require 'fileutils'
|
|
12
|
+
|
|
13
|
+
require 'eb_deployer/version'
|
|
14
|
+
require 'eb_deployer/utils'
|
|
15
|
+
require 'eb_deployer/aws_driver'
|
|
16
|
+
require 'eb_deployer/deployment_strategy'
|
|
17
|
+
require 'eb_deployer/cloud_formation_provisioner'
|
|
18
|
+
require 'eb_deployer/application'
|
|
19
|
+
require 'eb_deployer/resource_stacks'
|
|
20
|
+
require 'eb_deployer/throttling_handling'
|
|
21
|
+
require 'eb_deployer/eb_environment'
|
|
22
|
+
require 'eb_deployer/environment'
|
|
23
|
+
require 'eb_deployer/default_component'
|
|
24
|
+
require 'eb_deployer/component'
|
|
25
|
+
require 'eb_deployer/eb_event_source'
|
|
26
|
+
require 'eb_deployer/cf_event_source'
|
|
27
|
+
require 'eb_deployer/event_poller'
|
|
28
|
+
require 'eb_deployer/package'
|
|
29
|
+
require 'eb_deployer/config_loader'
|
|
30
|
+
require 'eb_deployer/default_config'
|
|
31
|
+
require 'eb_deployer/smoke_test'
|
|
32
|
+
require 'eb_deployer/version_cleaner'
|
|
33
|
+
|
|
34
|
+
module EbDeployer
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Query ouput value of the cloud formation stack
|
|
38
|
+
#
|
|
39
|
+
# @param [String] key CloudFormation output key
|
|
40
|
+
# @param [Hash] opts
|
|
41
|
+
# @option opts [Symbol] :application application name
|
|
42
|
+
# @option opts [Symbol] :environment environment name (e.g. staging, production)
|
|
43
|
+
# @option opts [Symbol] :region AWS Region (e.g. "us-west-2", "us-east-1")
|
|
44
|
+
#
|
|
45
|
+
def self.query_resource_output(key, opts)
|
|
46
|
+
if region = opts[:region]
|
|
47
|
+
Aws.config.update({
|
|
48
|
+
region: region
|
|
49
|
+
})
|
|
50
|
+
end
|
|
51
|
+
app = opts[:application]
|
|
52
|
+
env_name = opts[:environment]
|
|
53
|
+
cf = opts[:cf_driver] || AWSDriver::CloudFormationDriver.new
|
|
54
|
+
stack_name = opts[:stack_name] || "#{app}-#{env_name}"
|
|
55
|
+
provisioner = CloudFormationProvisioner.new(stack_name, cf)
|
|
56
|
+
provisioner.output(key)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
#
|
|
61
|
+
# Deploy a package to specified environments on elastic beanstalk
|
|
62
|
+
#
|
|
63
|
+
# @param [Hash] opts
|
|
64
|
+
#
|
|
65
|
+
# @option opts [Symbol] :application *required* Application name, this
|
|
66
|
+
# used for isolate packages and contribute to your elastic beanstalk cname
|
|
67
|
+
# for environments
|
|
68
|
+
#
|
|
69
|
+
# @option opts [Symbol] :environment *required* Environment for same
|
|
70
|
+
# application, e.g. testing, staging, production. This will map to 2 elastic
|
|
71
|
+
# beanstalk environments (env-a-xxx, env-b-xxx) if blue-green deployment
|
|
72
|
+
# strategy specified
|
|
73
|
+
#
|
|
74
|
+
# @option opts [Symbol] :package *required* package for the application
|
|
75
|
+
# which should be suitable for elastic beanstalk deploying. For example, a
|
|
76
|
+
# war file should be provided for java solution stacks and a ZIP file
|
|
77
|
+
# should be provided for Rails or Sinatra stack.
|
|
78
|
+
#
|
|
79
|
+
# @option opts [Symbol] :option_settings (optional) Elastic Beanstalk
|
|
80
|
+
# settings that will apply to the environments you deploying. Value should be
|
|
81
|
+
# array of hash with format such as:
|
|
82
|
+
#
|
|
83
|
+
# [{
|
|
84
|
+
# :namespace => 'aws:autoscaling:launchconfiguration',
|
|
85
|
+
# :option_name => 'InstanceType',
|
|
86
|
+
# :value => 'm1.small' }]
|
|
87
|
+
#
|
|
88
|
+
# When there are many, using an external yaml file to hold those
|
|
89
|
+
# configuration is recommended. Such as:
|
|
90
|
+
#
|
|
91
|
+
# YAML.load(File.read("my_settings_file.yml"))
|
|
92
|
+
#
|
|
93
|
+
# For all available options take a look at
|
|
94
|
+
# http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
|
|
95
|
+
#
|
|
96
|
+
# @option opts [Symbol] :accepted_healthy_states (['Green']) If :accepted_healthy_states
|
|
97
|
+
# is specified, EBDeployer will accept provided values when checking
|
|
98
|
+
# health of an environment instead of default value 'Green'. You can use it
|
|
99
|
+
# to specify additional healthy states, for example: ['Green', "Yellow"]
|
|
100
|
+
#
|
|
101
|
+
# @option opts [Symbol] :phoenix_mode (false) If phoenix mode is turn on, it
|
|
102
|
+
# will terminate the old elastic beanstalk environment and recreate on
|
|
103
|
+
# deploy. For blue-green deployment it terminate the inactive environment
|
|
104
|
+
# first then recreate it. This is useful to avoiding configuration drift and
|
|
105
|
+
# accumulating state on the EC2 instances. Also it has the benefit of keeping
|
|
106
|
+
# your EC2 instance system package upto date, because every time EC2 instance
|
|
107
|
+
# boot up from AMI it does a system update.
|
|
108
|
+
#
|
|
109
|
+
#
|
|
110
|
+
# @option opts [Symbol] :region set the region for application deployment
|
|
111
|
+
# (e.g. "us-west-2", "us-east-1"). See available zones at
|
|
112
|
+
# http://aws.amazon.com/elasticbeanstalk/faqs/#regions
|
|
113
|
+
#
|
|
114
|
+
# @option opts [Symbol] :resources If :resources specified, EBDeployer will
|
|
115
|
+
# use the CloudFormation template you provide to create a default
|
|
116
|
+
# CloudFormation stack with name <application_name>-<env-name> for the
|
|
117
|
+
# environment current deploying. Value of resources need to be hash with
|
|
118
|
+
# following keys:
|
|
119
|
+
#
|
|
120
|
+
# :template => CloudFormation template file with JSON format
|
|
121
|
+
# :policy => CloudFormation policy file with JSON format
|
|
122
|
+
# :override_policy => (false) If override_policy is true and a policy file is provided then the
|
|
123
|
+
# policy will temporarily override any existing policy on the resource stack during this update,
|
|
124
|
+
# otherwise the provided policy will replace any existing policy on the resource stack
|
|
125
|
+
# :parameters (or :inputs) => A Hash, input values for the CloudFormation template
|
|
126
|
+
# :transforms => A Hash with key map to your CloudFormation
|
|
127
|
+
# template outputs and value as lambda that return a single or array of
|
|
128
|
+
# elastic beanstalk settings.
|
|
129
|
+
#
|
|
130
|
+
# :capabilities => An array. You need set it to ['CAPABILITY_IAM']
|
|
131
|
+
#
|
|
132
|
+
# if you want to provision IAM Instance Profile.
|
|
133
|
+
#
|
|
134
|
+
# @option opts [Symbol] :settings See `option_settings`
|
|
135
|
+
#
|
|
136
|
+
# @option opts [Symbol] :package_bucket Name of s3 bucket where uploaded application
|
|
137
|
+
# packages will be stored. Note that the string ".packages" will be added as
|
|
138
|
+
# a suffix to your bucket. So, if "thoughtworks.simple" is passed as the bucket name,
|
|
139
|
+
# the actual s3 bucket name will be thoughtworks.simple.packages.
|
|
140
|
+
#
|
|
141
|
+
# @option opts [Symbol] :smoke_test Value should be a proc or a lambda which
|
|
142
|
+
# accept single argument that will passed in as environment DNS name. Smoke
|
|
143
|
+
# test proc or lambda will be called at the end of the deployment for
|
|
144
|
+
# inplace-update deployment strategy. For blue-green deployment it will run
|
|
145
|
+
# after inactive environment update finish and before switching. Defining a
|
|
146
|
+
# smoke test is high recommended for serious usage. The simplest one could
|
|
147
|
+
# just be checking the server is up using curl, e.g.
|
|
148
|
+
#
|
|
149
|
+
# :smoke_test => lambda { |host|
|
|
150
|
+
# curl_http_code = "curl -k -s -o /dev/null -w \"%{http_code}\" https://#{host}"
|
|
151
|
+
# Timeout.timeout(600) do
|
|
152
|
+
# while `#{curl_http_code}`.strip != '200'
|
|
153
|
+
# sleep 5
|
|
154
|
+
# end
|
|
155
|
+
# end
|
|
156
|
+
# }
|
|
157
|
+
#
|
|
158
|
+
# @option opts [Symbol] :strategy (:blue-green) There are two options:
|
|
159
|
+
# blue-green or inplace-update. Blue green keep two elastic beanstalk
|
|
160
|
+
# environments and always deploy to inactive one, to achieve zero downtime.
|
|
161
|
+
# inplace-update strategy will only keep one environment, and update the
|
|
162
|
+
# version inplace on deploy. this will save resources but will have downtime.
|
|
163
|
+
#
|
|
164
|
+
# @option opts [Symbol] :solution_stack_name ("64bit Amazon Linux 2013.09 running Tomcat 7 Java 7")
|
|
165
|
+
# The elastic beanstalk solution stack you want to deploy on top of.
|
|
166
|
+
#
|
|
167
|
+
# @option opts [Symbol] :tier ("WebServer")
|
|
168
|
+
# The environment tier. Either "WebServer" or "Worker"
|
|
169
|
+
#
|
|
170
|
+
# @option opts [Symbol] :version_label *required*. Version label give the
|
|
171
|
+
# package uploaded a unique identifier. Should use something related to
|
|
172
|
+
# pipeline counter if you have build pipeline setup to build the installer.
|
|
173
|
+
# For the convenience of dev we recommend use md5 digest of the installer so
|
|
174
|
+
# that every time you upload new installer it forms a new version. e.g.
|
|
175
|
+
#
|
|
176
|
+
# :version_label => ENV['MY_PIPELINE_COUNTER']
|
|
177
|
+
# || "dev-" + Digest::MD5.file(my_package).hexdigest
|
|
178
|
+
#
|
|
179
|
+
# @options opts [Symbol] :version_prefix. Specifies a prefix to prepend to the
|
|
180
|
+
# version label. This can be useful if you want to use different binaries for
|
|
181
|
+
# different environments.
|
|
182
|
+
#
|
|
183
|
+
# @option opts [Symbol] :keep_latest. Specifies the maximum number of versions to
|
|
184
|
+
# keep. Older versions are removed and deleted from the S3 source bucket as well.
|
|
185
|
+
# If specified as zero or not specified, all versions will be kept. If a
|
|
186
|
+
# version_prefix is given, only removes version starting with the prefix.
|
|
187
|
+
#
|
|
188
|
+
# @option opts [Symbol] :template_name. Specifies the environement template you wish
|
|
189
|
+
# to use to build your environment.
|
|
190
|
+
def self.deploy(opts)
|
|
191
|
+
if region = opts[:region]
|
|
192
|
+
Aws.config.update(:region => region)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
bs = opts[:bs_driver] || AWSDriver::Beanstalk.new
|
|
196
|
+
bs = ThrottlingHandling.new(bs, Aws::ElasticBeanstalk::Errors::Throttling)
|
|
197
|
+
s3 = opts[:s3_driver] || AWSDriver::S3Driver.new
|
|
198
|
+
cf = opts[:cf_driver] || AWSDriver::CloudFormationDriver.new
|
|
199
|
+
|
|
200
|
+
app_name = opts[:application]
|
|
201
|
+
env_name = opts[:environment]
|
|
202
|
+
version_prefix = opts[:version_prefix].to_s.strip
|
|
203
|
+
version_label = "#{version_prefix}#{opts[:version_label].to_s.strip}"
|
|
204
|
+
|
|
205
|
+
application = Application.new(app_name, bs, s3, opts[:package_bucket])
|
|
206
|
+
resource_stacks = ResourceStacks.new(opts[:resources],
|
|
207
|
+
cf,
|
|
208
|
+
!!opts[:skip_resource_stack_update],
|
|
209
|
+
opts[:tags])
|
|
210
|
+
|
|
211
|
+
stack_name = opts[:stack_name] || "#{app_name}-#{env_name}"
|
|
212
|
+
|
|
213
|
+
environment = Environment.new(application, env_name, stack_name, bs) do |env|
|
|
214
|
+
env.resource_stacks = resource_stacks
|
|
215
|
+
env.settings = opts[:option_settings] || opts[:settings] || []
|
|
216
|
+
env.inactive_settings = opts[:inactive_settings] || []
|
|
217
|
+
env.creation_opts = {
|
|
218
|
+
:template_name => opts[:template_name],
|
|
219
|
+
:solution_stack => opts[:solution_stack_name],
|
|
220
|
+
:cname_prefix => opts[:cname_prefix],
|
|
221
|
+
:smoke_test => opts[:smoke_test],
|
|
222
|
+
:phoenix_mode => opts[:phoenix_mode],
|
|
223
|
+
:accepted_healthy_states => opts[:accepted_healthy_states],
|
|
224
|
+
:blue_green_terminate_inactive => opts[:blue_green_terminate_inactive] || false,
|
|
225
|
+
:blue_green_terminate_inactive_wait => opts[:blue_green_terminate_inactive_wait] || 600,
|
|
226
|
+
:blue_green_terminate_inactive_sleep => opts[:blue_green_terminate_inactive_sleep] || 15,
|
|
227
|
+
:tags => opts[:tags],
|
|
228
|
+
:tier => opts[:tier]
|
|
229
|
+
}
|
|
230
|
+
env.strategy_name = opts[:strategy] || :blue_green
|
|
231
|
+
env.components = opts[:components]
|
|
232
|
+
env.component_under_deploy = opts[:component]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
application.create_version(version_label, opts[:package])
|
|
236
|
+
environment.deploy(version_label)
|
|
237
|
+
application.clean_versions(version_prefix, opts[:keep_latest].to_i || 0)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def self.destroy(opts)
|
|
241
|
+
if region = opts[:region]
|
|
242
|
+
Aws.config.update(:region => region)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
app = opts[:application]
|
|
246
|
+
bs = opts[:bs_driver] || AWSDriver::Beanstalk.new
|
|
247
|
+
s3 = opts[:s3_driver] || AWSDriver::S3Driver.new
|
|
248
|
+
|
|
249
|
+
Application.new(app, bs, s3).delete(opts[:environment])
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.cli
|
|
253
|
+
options = {
|
|
254
|
+
:action => :deploy,
|
|
255
|
+
:environment => 'dev',
|
|
256
|
+
:config_file => 'config/eb_deployer.yml'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
parser = cli_parser(options)
|
|
260
|
+
parser.parse!
|
|
261
|
+
action = options.delete(:action)
|
|
262
|
+
|
|
263
|
+
if File.exist?(options[:config_file])
|
|
264
|
+
puts "Found configuration at #{options[:config_file]}."
|
|
265
|
+
else
|
|
266
|
+
puts "Generated default configuration at #{options[:config_file]}."
|
|
267
|
+
DefaultConfig.new(File.basename(Dir.pwd)).write_to(options[:config_file])
|
|
268
|
+
exit(2)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
if !options[:package] && action == :deploy
|
|
272
|
+
puts "Missing options: -p (--package)"
|
|
273
|
+
puts "'eb_deploy --help' for details"
|
|
274
|
+
puts parser
|
|
275
|
+
exit(-1)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
self.send(action, ConfigLoader.new.load(options))
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
private
|
|
282
|
+
|
|
283
|
+
def self.cli_parser(options)
|
|
284
|
+
OptionParser.new do |opts|
|
|
285
|
+
opts.banner = "Usage: eb_deployer [options]"
|
|
286
|
+
opts.on("-p", "--package [FILE/S3_OBJECT]", "Package to deploy, can be a war file for java application, or yaml specification for package location on S3, or a S3 object & bucket name separated by colon, e.g. bucket_name:key_name") do |v|
|
|
287
|
+
options[:package] = v
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
opts.on("-e", "--environment [ENV_NAME]", "(Defaults to 'dev') Environment on which to operate (e.g. dev, staging, production). This must be defined in 'environments' section of the config file") do |v|
|
|
291
|
+
options[:environment] = v
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
opts.on("-c", "--config-file [FILE]", "eb_deployer config file. Default location is config/eb_deployer.yml") do |v|
|
|
295
|
+
options[:config_file] = v
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
opts.on("-d", "--destroy", "Destroy all Elasticbeanstalk environments under the application which have specified environment as name prefix") do |v|
|
|
299
|
+
options[:action] = :destroy
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
opts.on("-s", "--stack-name [STACK_NAME]", "CloudFormation stack name to use. If not specified, defaults to {app}-{env_name}") do |v|
|
|
303
|
+
options[:stack_name] = v
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
opts.on("--skip-resource-stack-update", "Skip cloud-formation stack update. (only for extreme situation like hitting a cloudformation bug)") do |v|
|
|
307
|
+
options[:skip_resource_stack_update] = true
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
opts.on("--component [COMPONENT]", "Specify which component to deploy") do |v|
|
|
311
|
+
options[:component] = v
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
opts.on("-v", "--version", "Print current version") do |v|
|
|
315
|
+
puts "eb_deployer v#{VERSION}"
|
|
316
|
+
exit(0)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
opts.on("--debug", "Output AWS debug log") do |d|
|
|
320
|
+
require 'logger'
|
|
321
|
+
logger = Logger.new($stdout)
|
|
322
|
+
logger.level = Logger::DEBUG
|
|
323
|
+
Aws.config.update(:logger => logger)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
opts.on("-h", "--help", "help") do
|
|
327
|
+
puts opts
|
|
328
|
+
puts ""
|
|
329
|
+
puts "S3 object package format: s3_bucket_name:s3_object_key"
|
|
330
|
+
puts "YAML package file format:"
|
|
331
|
+
puts "s3_bucket: <bucket_name>"
|
|
332
|
+
puts "s3_key: <object_path>"
|
|
333
|
+
exit(0)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'eb_deployer/default_config'
|
|
3
|
+
require 'aws-sdk-elasticbeanstalk'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
|
|
6
|
+
module EbDeployer
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
DEFAULT_STACK_NAME = '64bit Amazon Linux 2014.09 v1.1.0 running Ruby 2.1 (Passenger Standalone)'
|
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
11
|
+
|
|
12
|
+
def do_install
|
|
13
|
+
in_root do
|
|
14
|
+
copy_file 'eb_deployer.rake', 'lib/tasks/eb_deployer.rake'
|
|
15
|
+
template 'eb_deployer.yml.erb', 'config/eb_deployer.yml'
|
|
16
|
+
setup_database
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def setup_database
|
|
22
|
+
gem 'pg'
|
|
23
|
+
setup_database_yml
|
|
24
|
+
copy_file 'postgres_rds.json', 'config/rds.json'
|
|
25
|
+
directory 'ebextensions', '.ebextensions'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def setup_database_yml
|
|
29
|
+
gsub_file('config/database.yml', /^production:.+/m) do |match|
|
|
30
|
+
prod_start = false
|
|
31
|
+
match.split("\n").map do |l|
|
|
32
|
+
case l
|
|
33
|
+
when /^production/
|
|
34
|
+
prod_start = true
|
|
35
|
+
"# #{l}"
|
|
36
|
+
when /^\s+/
|
|
37
|
+
prod_start ? "# #{l}" : l
|
|
38
|
+
else
|
|
39
|
+
prod_start = false
|
|
40
|
+
l
|
|
41
|
+
end
|
|
42
|
+
end.join("\n")
|
|
43
|
+
end
|
|
44
|
+
append_to_file('config/database.yml', <<-YAML)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
production:
|
|
48
|
+
adapter: postgresql
|
|
49
|
+
database: <%= ENV['DATABASE_NAME'] || '#{app_name}_production' %>
|
|
50
|
+
host: <%= ENV['DATABASE_HOST'] || 'localhost' %>
|
|
51
|
+
port: <%= ENV['DATABASE_PORT'] || 5432 %>
|
|
52
|
+
username: <%= ENV['DATABASE_USERNAME'] || #{ENV['USER'].inspect} %>
|
|
53
|
+
password: <%= ENV['DATABASE_PASSWORD'] %>
|
|
54
|
+
min_messages: ERROR
|
|
55
|
+
YAML
|
|
56
|
+
end
|
|
57
|
+
def db_password
|
|
58
|
+
"PleaseChangeMe"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def solution_stack_name
|
|
62
|
+
Aws::ElasticBeanstalk.Client.new.list_available_solution_stacks[:solution_stacks].find do |s|
|
|
63
|
+
s =~ /Amazon Linux/ && s =~ /running Ruby 2.1 \(Passenger Standalone\)/
|
|
64
|
+
end
|
|
65
|
+
rescue
|
|
66
|
+
DEFAULT_STACK_NAME
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def alphanumeric_name
|
|
70
|
+
app_name.gsub(/-/, '')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def secure_random(length)
|
|
74
|
+
SecureRandom.hex(length)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def app_name
|
|
78
|
+
File.basename(Dir.pwd).downcase.gsub(/[^0-9a-z]/, '-').gsub(/--/, '-')
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'eb_deployer/tasks'
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# application name
|
|
2
|
+
application: <%= app_name %>
|
|
3
|
+
|
|
4
|
+
# common settings for all environments
|
|
5
|
+
common:
|
|
6
|
+
# Solution stack for elastic beanstalk, default is 64bit tomcat 7 for JAVA app
|
|
7
|
+
solution_stack_name: <%= solution_stack_name %>
|
|
8
|
+
|
|
9
|
+
# Tier name for environments. Current supported values are WebServer and Worker
|
|
10
|
+
# tier: WebServer
|
|
11
|
+
|
|
12
|
+
# AWS region to deploy. Default to us-east-1
|
|
13
|
+
# region: us-west-1
|
|
14
|
+
|
|
15
|
+
# There are two deployment strategies: 'blue-green' or 'inplace-update'.
|
|
16
|
+
# Blue green deployments keep two elastic beanstalk environments and always deploy to
|
|
17
|
+
# inactive one, to achieve zero downtime.
|
|
18
|
+
# Inplace-update strategy will only keep one environment, and update the version inplace on
|
|
19
|
+
# deploy. Inplace-update will save resources but will suffer from downtime.
|
|
20
|
+
# (All old environments need be destroyed when you switching between strategies.)
|
|
21
|
+
# Default strategy is 'blue-green'.
|
|
22
|
+
# strategy: blue-green
|
|
23
|
+
|
|
24
|
+
# Name of s3 bucket where uploaded application packages will be stored.
|
|
25
|
+
# Note that the string ".packages" will be added as a suffix to your bucket.
|
|
26
|
+
# So, if "thoughtworks.simple" is passed as the bucket name, the actual s3 bucket
|
|
27
|
+
# name will be thoughtworks.simple.packages. Default to application name.
|
|
28
|
+
# package_bucket: my-s3-bucket
|
|
29
|
+
|
|
30
|
+
# If phoenix mode is turned 'on', it will terminate the old elastic
|
|
31
|
+
# beanstalk environment and recreate a new one on deploy. For blue-green
|
|
32
|
+
# deployment it will terminate the inactive environment first then
|
|
33
|
+
# recreate it. This is useful to avoid configuration drift and
|
|
34
|
+
# accumulating state on the ec2 instances. Also it has the benefit of
|
|
35
|
+
# keeping your ec2 instance system package upto date, because everytime ec2
|
|
36
|
+
# instance boots up from AMI it does a system update. Default is 'off' but we suggest
|
|
37
|
+
# you override it to 'on' for production environment.
|
|
38
|
+
# phoenix_mode: false
|
|
39
|
+
|
|
40
|
+
# Terminates the inactive stack upon successful blue-green deployment.
|
|
41
|
+
# This is useful if you require HA deployment but don't want the cost of running
|
|
42
|
+
# a stack that is not in use. See https://github.com/ThoughtWorksStudios/eb_deployer/pull/44/
|
|
43
|
+
# for other strategies that can be employed.
|
|
44
|
+
# Note: As EB Deployer uses DNS to switch between stacks, this should be used only if you
|
|
45
|
+
# both put in a sufficient wait period and accept the risk that traffic may still be
|
|
46
|
+
# going to the inactive stack when it is terminated, due to client DNS caching, for example.
|
|
47
|
+
#blue_green_terminate_inactive: false
|
|
48
|
+
|
|
49
|
+
# How long to wait before terminating the inactive stack if 'blue_green_terminate_inactive' is set to true.
|
|
50
|
+
#blue_green_terminate_inactive_wait: 600
|
|
51
|
+
|
|
52
|
+
# How often to check for changes to the inactive stack during the 'blue_green_terminate_inactive_wait' period.
|
|
53
|
+
#blue_green_terminate_inactive_sleep: 15
|
|
54
|
+
|
|
55
|
+
# Specifies the maximum number of versions to keep. Older versions are removed
|
|
56
|
+
# and deleted from the S3 source bucket as well. If specified as zero or not
|
|
57
|
+
# specified, all versions will be kept. If a version_prefix is given, only removes
|
|
58
|
+
# version starting with the prefix.
|
|
59
|
+
# keep_latest: 200
|
|
60
|
+
|
|
61
|
+
# Specifies a prefix to prepend to the version label.
|
|
62
|
+
# This can be useful if you want to use different binaries for different
|
|
63
|
+
# environments.
|
|
64
|
+
# version_prefix:
|
|
65
|
+
|
|
66
|
+
# Generating version label for package to be deployed. A readable version label will
|
|
67
|
+
# provide better traceablity of your deployment process.
|
|
68
|
+
# By default setting is:
|
|
69
|
+
# version_label: <%%= package_digest %>
|
|
70
|
+
# which means using MD5 digest of the package file. If you deploy using build
|
|
71
|
+
# pipeline tool such as GO, switching to pipline counter is highly suggested to
|
|
72
|
+
# increase the readability. Following example will read pipeline counter from environment
|
|
73
|
+
# variable with a fall back to digest for local deployment:
|
|
74
|
+
# version_label: <%%= ENV['GO_PIPELINE_COUNTER'] || package_digest %>
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Smoke test value should be a piece of ruby code with access to single variable
|
|
78
|
+
# "host_name" -- environment DNS name. Smoke test snippet will be evaluated at
|
|
79
|
+
# the end of the deployment for inplace-update deployment. For blue-green
|
|
80
|
+
# deployment it will run after inactive environment update is completed and before
|
|
81
|
+
# switching over.
|
|
82
|
+
# Defining a smoke test is highly recommended for serious usage. By default we use
|
|
83
|
+
# The simplest one that just be checking server landing page using curl, e.g.
|
|
84
|
+
smoke_test: |
|
|
85
|
+
curl_http_code = "curl -s -o /dev/null -w \"%{http_code}\" http://#{host_name}"
|
|
86
|
+
Timeout.timeout(600) do
|
|
87
|
+
until ['200', '301', '302'].include?(`#{curl_http_code}`.strip)
|
|
88
|
+
sleep 5
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# List of Elastic Beanstalk health states that will be considered healthy when deploying.
|
|
93
|
+
# You may want to override default 'Green' with that list, if for example you are using
|
|
94
|
+
# Elastic Beanstalk worker tier and you don't want eb_deployer to fail if you have messages
|
|
95
|
+
# in your SQS queue.
|
|
96
|
+
# By default eb_deployer only considers 'Green' as the healthy state.
|
|
97
|
+
# For a list of all Elastic Beanstalk health states refer to:
|
|
98
|
+
# http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.healthstatus.html#using-features.healthstatus.colors
|
|
99
|
+
# accepted_healthy_states:
|
|
100
|
+
# - Green
|
|
101
|
+
# - Yellow
|
|
102
|
+
|
|
103
|
+
# Elastic Beanstalk settings that will apply to the environments you are
|
|
104
|
+
# deploying.
|
|
105
|
+
# For all available options take a look at
|
|
106
|
+
# http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
|
|
107
|
+
option_settings:
|
|
108
|
+
# Following is an example of set EC2 ssh key name. This allow you ssh into the ec2
|
|
109
|
+
# instance. See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html
|
|
110
|
+
# - namespace: aws:autoscaling:launchconfiguration
|
|
111
|
+
# option_name: EC2KeyName
|
|
112
|
+
# value: <my-ec2-key-name>
|
|
113
|
+
|
|
114
|
+
# Following is an example which changes EC2 instance type
|
|
115
|
+
# from t1.micro (default) to m1.small. Instances with t1.micro type sometime
|
|
116
|
+
# are not very responsible, so m1.small is suggested for saving time.
|
|
117
|
+
# But if you care about the marginal cost difference you can comment this out to
|
|
118
|
+
# go back to t1.micro.
|
|
119
|
+
- namespace: aws:autoscaling:launchconfiguration
|
|
120
|
+
option_name: InstanceType
|
|
121
|
+
value: m1.small
|
|
122
|
+
- namespace: aws:elasticbeanstalk:application:environment
|
|
123
|
+
option_name: DATABASE_NAME
|
|
124
|
+
value: <%= alphanumeric_name %>
|
|
125
|
+
- namespace: aws:elasticbeanstalk:application:environment
|
|
126
|
+
option_name: DATABASE_USERNAME
|
|
127
|
+
value: <%= alphanumeric_name %>
|
|
128
|
+
- namespace: aws:elasticbeanstalk:application:environment
|
|
129
|
+
option_name: DATABASE_PASSWORD
|
|
130
|
+
value: <%= db_password %>
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# If resources specified, eb_deployer will use the CloudFormation
|
|
134
|
+
# template you provide to create a default CloudFormation stack with
|
|
135
|
+
# name <application_name>-<env-name> for the environment current
|
|
136
|
+
# deploying. And Outputs of the CloudFormation can be mapped to Elastic Beanstalk
|
|
137
|
+
# options settings.
|
|
138
|
+
# keys:
|
|
139
|
+
# template => CloudFormation template file with JSON format
|
|
140
|
+
# policy => CloudFormation policy file with JSON format
|
|
141
|
+
# override_policy => (false) If override_policy is true and a policy file is provided then the policy will temporarily override any existing policy on the resource stack during this update,
|
|
142
|
+
# otherwise the provided policy will replace any existing policy on the resource stack
|
|
143
|
+
# inputs => A Hash, input values for the CloudFormation template
|
|
144
|
+
# outputs => A Hash with key map to your CloudFormation template outputs and value as elastic beanstalk settings namespace and option_name.
|
|
145
|
+
# capabilities => An array. You need set it to ['CAPABILITY_IAM'] if the
|
|
146
|
+
# template include IAM Instance Profile.
|
|
147
|
+
resources:
|
|
148
|
+
# Creating a RDS instance by CloudFormation, so that database will not hook up with any ElasticBeanstalk environment
|
|
149
|
+
template: config/rds.json
|
|
150
|
+
inputs:
|
|
151
|
+
DBName: <%= alphanumeric_name %>
|
|
152
|
+
DBUser: <%= alphanumeric_name %>
|
|
153
|
+
DBPassword: <%= db_password %>
|
|
154
|
+
outputs:
|
|
155
|
+
RDSPassSecurityGroup:
|
|
156
|
+
namespace: aws:autoscaling:launchconfiguration
|
|
157
|
+
option_name: SecurityGroups
|
|
158
|
+
RDSHost:
|
|
159
|
+
namespace: aws:elasticbeanstalk:application:environment
|
|
160
|
+
option_name: DATABASE_HOST
|
|
161
|
+
RDSPort:
|
|
162
|
+
namespace: aws:elasticbeanstalk:application:environment
|
|
163
|
+
option_name: DATABASE_PORT
|
|
164
|
+
|
|
165
|
+
# You can define environment here. Each environment can overriden any common settings
|
|
166
|
+
environments:
|
|
167
|
+
dev:
|
|
168
|
+
# overriding common settings
|
|
169
|
+
strategy: inplace-update
|
|
170
|
+
production:
|
|
171
|
+
option_settings:
|
|
172
|
+
# example for overriding common option_settings: providing least redundancy
|
|
173
|
+
# in production environment.
|
|
174
|
+
# - namespace: aws:autoscaling:asg
|
|
175
|
+
# option_name: MinSize
|
|
176
|
+
# value: "2"
|
|
177
|
+
|
|
178
|
+
# For Rails4: config/secrets.yml
|
|
179
|
+
- namespace: aws:elasticbeanstalk:application:environment
|
|
180
|
+
option_name: SECRET_KEY_BASE
|
|
181
|
+
value: <%= secure_random(128) %>
|