eb_deployer 0.0.12 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 tworker
1
+ Copyright (c) 2013 Thoughtworks Inc.
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # EbDeployer
2
2
 
3
- TODO: Write a gem description
3
+ Low friction deployments should be a breeze. Elastic Beanstalk provides a great foundation for performing Blue-Green deployments, and EbDeployer add a missing top to automate the whole flow out of box.
4
+
5
+ ElasticBeanstalk Deployer thus allows you to do continuous delivery on AWS.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,7 +20,84 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- TODO: Write usage instructions here
23
+ ### Step One: AWS Account Setup
24
+
25
+ Create an AWS IAM user for deploy and give it privilege to operate Elastic Beanstalk. Download the access keys for executing the deployment tasks later.
26
+
27
+ ### Step Two: Packaging
28
+
29
+ You need package your application for Elastic Beanstalk stack first. For Java app an warball is appropriate. For Ruby on Rails app a tar.gz file is good. You can also package a Rails/Sinatra app as war ball using warbler and deploy to Java stack.
30
+
31
+
32
+ ### Step Three: Define the task
33
+ Add a deploy task for deployment in your Rakefile
34
+
35
+ require 'digest'
36
+
37
+ desc "deploy our beloved app to elastic beanstalk"
38
+ task :deploy, [:package] do |t, args|
39
+ EbDeployer.deploy(:application => "MYAPP",
40
+ :environment => "production",
41
+ :solution_stack_name => <SOLUTION_STACK_NAME>
42
+ :package => args[:package],
43
+ :version_label => "dev-" + Digest::MD5.file(args[:package]).hexdigest)
44
+ end
45
+
46
+ ### Step Four: Fasten your seat belt
47
+ run deploy task:
48
+
49
+ rake deploy[<package built>] AWS_ACCESS_KEY_ID=<deployers_aws_key> AWS_SECRET_ACCESS_KEY=<secret>
50
+ Then open aws console for Elastic Beanstalk to see what happened.
51
+
52
+
53
+ ### Smoke Testing your stack
54
+
55
+ EB_Deployer allows you to automate your deployment and then some. You can also add smoke tests to your deployment - thus ensuring that the app you deployed is also working correctly.
56
+ Adding a smoke test suite is also simple. All that you need to do is edit your rake task as follows:
57
+
58
+ desc "deploy our simple java app with one page"
59
+ task :deploy, [:package] do |t, args|
60
+ EbDeployer.deploy(:application => "MYAPP",
61
+ :environment => "production",
62
+ :solution_stack_name => <SOLUTION_STACK_NAME>
63
+ :package => args[:package],
64
+ :version_label => "dev-" + Digest::MD5.file(args[:package]).hexdigest)
65
+ :smoke_test => lambda { |host|
66
+ Timeout.timeout(600) do
67
+ until `curl http://#{host}`.include?('Hello, World')
68
+ sleep 5
69
+ end
70
+ end
71
+ })
72
+ end
73
+ You can add more smoke tests by calling arbitrary tasks from this rake task.
74
+ Smoke testing gets you one step closer to continuous delivery.
75
+
76
+ ### Blue-Green deployment
77
+ Since every deployment now runs smoke test, you now have a better safety net around your deployments. This allows us to trigger automatic blue-green deployments.
78
+
79
+ To do this you need not do anything special. So far we have deployed the application only once. Let's call this the 'green' stack. Any subsequent calls to deployment will deployment a copy of this application to a new stack - the 'blue' stack. Smoke tests will be run on it and once everything passes the 'blue'(new) stack will be switched to the 'green' stack. Thus your new code will now be on the active stack and the user will experience no downtime.
80
+
81
+ Once this new stack is stable or has run for a while you can choose to delete the old stack. Or if you are doing continuous delivery you may be ready to another 'blue' deployment. You could just trigger another deployment and repeat this every hour/day/week... you get the idea.
82
+
83
+
84
+
85
+ ### Destroying a stack
86
+ So you are done with this application or environment, you can destroy it easily as well.
87
+
88
+ desc "clean up everything"
89
+ task :teardown do |t, args|
90
+ EbDeployer.destroy(:application => "ebtest-simple")
91
+ end
92
+
93
+ and you are done!
94
+
95
+ Later tutorials coming soon will cover
96
+ * how to setup multiple environment suites: production, staging, and how to manage configurations for them
97
+ * how to setup RDS or other AWS resource and share them between blue green environments
98
+
99
+ Take a look at code if you can not wait for the documentation.
100
+
22
101
 
23
102
  ## Contributing
24
103
 
data/eb_deployer.gemspec CHANGED
@@ -4,14 +4,14 @@ require File.expand_path('../lib/eb_deployer/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["wpc", "betarelease"]
6
6
  gem.email = ["alex.hal9000@gmail.com", "sudhindra.r.rao@gmail.com"]
7
- gem.description = %q{Elastic Beanstalk Deployer with different deployment strategies.}
8
- gem.summary = %q{Pick strategies like InplaceUpdate, Blue/Green.}
7
+ gem.description = %q{For automate blue green deployment flow on Elasti Beanstalk.}
8
+ gem.summary = %q{Low friction deployments should be a breeze. Elastic Beanstalk provides a great foundation for performing Blue-Green deployments, and EbDeployer add a missing top to automate the whole flow out of box.}
9
9
  gem.homepage = "https://github.com/ThoughtWorksStudios/eb_deployer"
10
10
 
11
11
  gem.add_runtime_dependency 'aws-sdk'
12
12
  gem.add_development_dependency 'minitest'
13
13
 
14
- gem.files = `git ls-files`.split($\)
14
+ gem.files = `git ls-files`.split($\).reject {|f| f =~ /^samples\// }
15
15
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
17
  gem.name = "eb_deployer"
data/lib/eb_deployer.rb CHANGED
@@ -28,6 +28,88 @@ module EbDeployer
28
28
  provisioner.output(key)
29
29
  end
30
30
 
31
+
32
+ #
33
+ # Options available:
34
+ #
35
+ # :application (required)
36
+ # Application name, this used for isolate packages and contribute
37
+ # to your elastic beanstalk cname for environments
38
+ #
39
+ # :environment (required)
40
+ # Environment for same application, e.g. testing, staging,
41
+ # production. This will map to 2 elastic beanstalk environments
42
+ # (env-a-xxx, env-b-xxx) if blue-green deployment strategy specified
43
+ #
44
+ # :package (required) package for the application which should be
45
+ # suitable for elastic beanstalk deploying. For example, a war file
46
+ # should be provided for java solution stacks and a tar gz file
47
+ # should be provided for rails stack.
48
+ #
49
+ # :version_label (required)
50
+ # Version label give the package uploaded a unique identifier.
51
+ # Should use something related to pipeline counter if you have build
52
+ # pipeline setup to build the installer. For the convient of dev we
53
+ # recommend use md5 digest of the installer so that everytime you
54
+ # upload new installer it forms a new version. e.g.
55
+ #
56
+ # :version_label => ENV['MY_PIPELINE_COUNTER']
57
+ # || "dev-" + Digest::MD5.file(my_package).hexdigest
58
+ #
59
+ # :solution_stack_name (optional default "64bit Amazon Linux running Tomcat 7")
60
+ # The elastic beanstalk solution stack you want to deploy on top of.
61
+ # Current possible values include:
62
+ #
63
+ # :settings (optional)
64
+ # Elastic Beanstalk settings that will apply to the environments you
65
+ # deploying. Value should be array of hash with format such as:
66
+ # [{
67
+ # :namespace => 'aws:autoscaling:launchconfiguration',
68
+ # :option_name => 'InstanceType',
69
+ # :value => 'm1.small' }]
70
+ # When there are many, Using an external yaml file to hold those
71
+ # configuration is recommended. Such as:
72
+ # YAML.load(File.read("my_settings_file.yml"))
73
+ # For all available options take a look at
74
+ # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
75
+ #
76
+ # :resources (optional)
77
+ # If :resources specified, EBDeployer will use the CloudFormation
78
+ # template you provide to create a default CloudFormation stack with
79
+ # name <application_name>-<env-name> for the environment current
80
+ # deploying. Value of resources need to be hash with following
81
+ # keys:
82
+ # :template => CloudFormation template file with JSON format
83
+ # :parameters => A Hash, input values for the CloudFormation
84
+ # template
85
+ # :transforms => A Hash with key map to your CloudFormation
86
+ # template outputs and value as lambda that return a single or array of
87
+ # elastic beanstalk settings.
88
+ # :capabilities => An array. You need set it to ['CAPABILITY_IAM']
89
+ # if you want to provision IAM Instance Profile.
90
+ #
91
+ # :smoke_test (optional)
92
+ # Value should be a proc or a lambda which accept single argument that will
93
+ # passed in as environment DNS name. Smoke test proc or lambda will be
94
+ # called at the end of the deployment for inplace-update deployment
95
+ # strategy. For blue-green deployment it will run after inactive
96
+ # environment update finish and before switching.
97
+ # Defining a smoke test is high recommended for serious usage. The
98
+ # simplest one could just be checking the server is up using curl, e.g.
99
+ #
100
+ # :smoke_test => lambda { |host|
101
+ # curl_http_code = "curl -k -s -o /dev/null -w \"%{http_code}\" https://#{host}"
102
+ # Timeout.timeout(600) do
103
+ # while `#{curl_http_code}`.strip != '200'
104
+ # sleep 5
105
+ # end
106
+ # end
107
+ # }
108
+ #
109
+ #
110
+ # deploy a package to specfied environments on elastic beanstalk
111
+ #
112
+
31
113
  def self.deploy(opts)
32
114
  # AWS.config(:logger => Logger.new($stdout))
33
115
  if region = opts[:region]
@@ -43,7 +125,7 @@ module EbDeployer
43
125
  version_label = opts[:version_label].to_s.strip
44
126
  cname = opts[:cname]
45
127
  env_settings = opts[:settings] || []
46
- strategy_name = opts[:strategy] || :inplace_update
128
+ strategy_name = opts[:strategy] || :blue_green
47
129
  cname_prefix = opts[:cname_prefix] || [app, env_name].join('-')
48
130
  smoke_test = opts[:smoke_test] || Proc.new {}
49
131
 
@@ -64,4 +146,16 @@ module EbDeployer
64
146
  strategy.deploy(version_label, env_settings)
65
147
  end
66
148
 
149
+ def self.destroy(opts)
150
+ if region = opts[:region]
151
+ AWS.config(:region => region)
152
+ end
153
+
154
+ app = opts[:application]
155
+ bs = opts[:bs_driver] || Beanstalk.new
156
+ s3 = opts[:s3_driver] || S3Driver.new
157
+ cf = opts[:cf_driver] || CloudFormationDriver.new
158
+ Application.new(app, bs, s3).delete
159
+ end
160
+
67
161
  end
@@ -17,6 +17,16 @@ module EbDeployer
17
17
  end
18
18
  end
19
19
 
20
+ def delete
21
+ if @eb_driver.application_exists?(@name)
22
+ @eb_driver.environment_names_for_application(@name).each do |env|
23
+ @eb_driver.delete_environment(@name, env)
24
+ end
25
+
26
+ @eb_driver.delete_application(@name)
27
+ end
28
+ end
29
+
20
30
  private
21
31
 
22
32
  def create_application_if_not_exists
@@ -9,6 +9,10 @@ module EbDeployer
9
9
  @client.create_application(:application_name => app)
10
10
  end
11
11
 
12
+ def delete_application(app)
13
+ @client.delete_application(:application_name => app)
14
+ end
15
+
12
16
  def application_exists?(app)
13
17
  @client.describe_applications(:application_names => [app])[:applications].any?
14
18
  end
@@ -24,6 +28,10 @@ module EbDeployer
24
28
  alive_envs(app_name, [env_name]).any?
25
29
  end
26
30
 
31
+ def environment_names_for_application(app_name)
32
+ alive_envs(app_name).collect { |env| env[:environment_name] }
33
+ end
34
+
27
35
  def create_environment(app_name, env_name, stack_name, cname_prefix, version, settings)
28
36
  request = {:application_name => app_name,
29
37
  :environment_name => env_name,
@@ -34,6 +42,10 @@ module EbDeployer
34
42
  @client.create_environment(request)
35
43
  end
36
44
 
45
+ def delete_environment(app_name, env_name)
46
+ @client.terminate_environment(:environment_name => env_name)
47
+ end
48
+
37
49
  def create_application_version(app_name, version_label, source_bundle)
38
50
  @client.create_application_version(:application_name => app_name,
39
51
  :source_bundle => source_bundle,
@@ -1,3 +1,3 @@
1
1
  module EbDeployer
2
- VERSION = "0.0.12"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -10,6 +10,11 @@ class EBStub
10
10
  @apps << app
11
11
  end
12
12
 
13
+ def delete_application(app)
14
+ raise "there are environments running for this app" unless environment_names_for_application(app).empty?
15
+ @apps.delete(app)
16
+ end
17
+
13
18
  def application_exists?(app)
14
19
  @apps.include?(app)
15
20
  end
@@ -17,13 +22,19 @@ class EBStub
17
22
  def create_environment(app, env, solution_stack, cname_prefix, version, settings)
18
23
  raise 'cname prefix is not avaible' if @envs.values.detect { |env| env[:cname_prefix] == cname_prefix }
19
24
  raise "env name #{env} is longer than 23 chars" if env.size > 23
25
+ raise "app not exists" unless application_exists?(app)
20
26
  @envs[env_key(app, env)] = {
27
+ :application => app,
21
28
  :solution_stack => solution_stack,
22
29
  :version => version,
23
30
  :cname_prefix => cname_prefix,
24
31
  :settings => settings}
25
32
  end
26
33
 
34
+ def delete_environment(app, env)
35
+ @envs.delete(env)
36
+ end
37
+
27
38
  def update_environment(app, env, version, settings)
28
39
  @envs[env_key(app, env)].merge!(:version => version, :settings => settings)
29
40
  end
@@ -78,7 +89,16 @@ class EBStub
78
89
  @envs[env_key(app_name, env_name)][:settings]
79
90
  end
80
91
 
92
+ def environment_names_for_application(app)
93
+ @envs.inject([]) do |memo, pair|
94
+ env_name, env = pair
95
+ memo << env_name if env[:application] == app
96
+ memo
97
+ end
98
+ end
99
+
81
100
  private
101
+
82
102
  def env_key(app, name)
83
103
  [app, name].join("-")
84
104
  end
data/test/deploy_test.rb CHANGED
@@ -19,6 +19,13 @@ class DeployTest < Minitest::Test
19
19
  assert @eb_driver.application_exists?('simple')
20
20
  end
21
21
 
22
+ def test_destroy_should_clean_up_eb_application_and_env
23
+ deploy(:application => 'simple', :environment => "production")
24
+ destroy(:application => 'simple')
25
+ assert !@eb_driver.application_exists?('simple')
26
+ assert !@eb_driver.environment_exists?('simple', eb_envname('simple', 'production'))
27
+ end
28
+
22
29
  def test_first_deployment_create_environment
23
30
  assert !@eb_driver.environment_exists?('simple', eb_envname('simple', 'production'))
24
31
  deploy(:application => 'simple', :environment => "production")
@@ -246,10 +253,20 @@ class DeployTest < Minitest::Test
246
253
 
247
254
  def deploy(opts)
248
255
  EbDeployer.deploy({:package => @sample_package,
249
- :bs_driver => @eb_driver,
250
- :s3_driver => @s3_driver,
251
- :cf_driver => @cf_driver,
252
- :version_label => 1}.merge(opts))
256
+ :strategy => :inplace_update,
257
+ :version_label => 1}.merge(opts).merge(stubs))
258
+ end
259
+
260
+ def destroy(opts)
261
+ EbDeployer.destroy(opts.merge(stubs))
262
+ end
263
+
264
+
265
+ def stubs
266
+ { :bs_driver => @eb_driver,
267
+ :s3_driver => @s3_driver,
268
+ :cf_driver => @cf_driver
269
+ }
253
270
  end
254
271
 
255
272
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eb_deployer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-08 00:00:00.000000000 Z
13
+ date: 2013-08-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -44,7 +44,7 @@ dependencies:
44
44
  - - ! '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
- description: Elastic Beanstalk Deployer with different deployment strategies.
47
+ description: For automate blue green deployment flow on Elasti Beanstalk.
48
48
  email:
49
49
  - alex.hal9000@gmail.com
50
50
  - sudhindra.r.rao@gmail.com
@@ -91,10 +91,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  version: '0'
92
92
  requirements: []
93
93
  rubyforge_project:
94
- rubygems_version: 1.8.24
94
+ rubygems_version: 1.8.25
95
95
  signing_key:
96
96
  specification_version: 3
97
- summary: Pick strategies like InplaceUpdate, Blue/Green.
97
+ summary: Low friction deployments should be a breeze. Elastic Beanstalk provides a
98
+ great foundation for performing Blue-Green deployments, and EbDeployer add a missing
99
+ top to automate the whole flow out of box.
98
100
  test_files:
99
101
  - test/aws_driver_stubs.rb
100
102
  - test/deploy_test.rb