eb_deployer 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,16 +6,6 @@ ElasticBeanstalk Deployer thus allows you to do continuous delivery on AWS.
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
10
-
11
- gem 'eb_deployer'
12
-
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
18
-
19
9
  $ gem install eb_deployer
20
10
 
21
11
  ## Usage
@@ -26,57 +16,50 @@ Create an AWS IAM user for deploy and give it privilege to operate Elastic Beans
26
16
 
27
17
  ### Step Two: Packaging
28
18
 
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.
19
+ 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. (Please remember do rake assets:precompile first for rails app)
20
+
30
21
 
22
+ ### Step Three: Generate configuration and Configure deployment process
31
23
 
32
- ### Step Three: Define the task
33
- Add a deploy task for deployment in your Rakefile
24
+ $ eb_deploy
34
25
 
35
- require 'digest'
26
+ This will generate a default configuration at location 'config/eb_deployer.yml'. It is almost empty but working one. And it will generate settings for two environment 'dev' and 'production'. If you had time please try to read through it to see the options you can tweak.
36
27
 
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
28
 
46
29
  ### Step Four: Fasten your seat belt
47
- run deploy task:
30
+ run deploy
31
+
32
+ $ eb_deploy -p <package built> -e <environment>
48
33
 
49
- rake deploy[<package built>] AWS_ACCESS_KEY_ID=<deployers_aws_key> AWS_SECRET_ACCESS_KEY=<secret>
50
34
  Then open aws console for Elastic Beanstalk to see what happened.
51
35
 
52
36
 
53
37
  ### Smoke Testing your stack
54
38
 
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.
39
+ 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.
40
+ Adding a smoke test suite is also simple. Check "smoke_test" section in your eb_deployer.yml. The simplest thing you can do is using curl make sure landing page get loaded, e.g.:
41
+
42
+ smoke_test: >
43
+ curl_http_code = "curl -s -o /dev/null -w \"%{http_code}\" http://#{host_name}"
44
+ Timeout.timeout(600) do
45
+ while `#{curl_http_code}`.strip != '200'
46
+ sleep 5
47
+ end
48
+ end
49
+
50
+
51
+ You can add more smoke tests by calling arbitrary rake tasks (Please make sure check return status):
52
+
53
+ smoke_test: >
54
+ `rake test:smoke HOST_NAME=#{host_name}`
55
+ raise("Smoke failed!") unless $?.success?
56
+
74
57
  Smoke testing gets you one step closer to continuous delivery.
75
-
58
+
76
59
  ### 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.
60
+ 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
61
 
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.
62
+ 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
63
 
81
64
  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
65
 
@@ -84,12 +67,9 @@ Once this new stack is stable or has run for a while you can choose to delete th
84
67
 
85
68
  ### Destroying a stack
86
69
  So you are done with this application or environment, you can destroy it easily as well.
70
+
71
+ $ eb_deployer -d -e <environment>
87
72
 
88
- desc "clean up everything"
89
- task :teardown do |t, args|
90
- EbDeployer.destroy(:application => "ebtest-simple")
91
- end
92
-
93
73
  and you are done!
94
74
 
95
75
  Later tutorials coming soon will cover
data/bin/eb_deploy ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ git_path = File.join(File.expand_path('../..', __FILE__), '.git')
4
+
5
+ if File.exists?(git_path)
6
+ lib_path = File.expand_path('../../lib', __FILE__)
7
+ $:.unshift(lib_path)
8
+ end
9
+
10
+ require 'eb_deployer'
11
+ EbDeployer.cli
@@ -26,7 +26,8 @@ module EbDeployer
26
26
  end
27
27
 
28
28
  def query_output(name, key)
29
- stack(name).outputs.find { |o| o.key == key }.try(:value)
29
+ output = stack(name).outputs.find { |o| o.key == key }
30
+ output && output.value
30
31
  end
31
32
 
32
33
  private
@@ -12,14 +12,17 @@ module EbDeployer
12
12
  end
13
13
 
14
14
  def provision(resources)
15
+ resources = symbolize_keys(resources)
15
16
  template = File.read(resources[:template])
16
- transforms = resources[:transforms]
17
+ outputs = resources[:outputs] || {}
18
+ transforms = resources[:transforms] || {}
17
19
  capabilities = resources[:capabilities] || []
18
- params = resources[:parameters] || {}
20
+ params = resources[:inputs] || resources[:parameters] || {}
19
21
 
20
22
  stack_exists? ? update_stack(template, params, capabilities) : create_stack(template, params, capabilities)
21
23
  wait_for_stack_op_terminate
22
- transform_output_to_settings(transforms)
24
+
25
+ transform_output_to_settings(convert_to_transforms(outputs).merge(transforms))
23
26
  end
24
27
 
25
28
  def output(key)
@@ -30,6 +33,19 @@ module EbDeployer
30
33
 
31
34
  private
32
35
 
36
+ #todo: remove duplication
37
+ def symbolize_keys(hash)
38
+ hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
39
+ end
40
+
41
+
42
+ def convert_to_transforms(outputs)
43
+ outputs.inject({}) do |memo, (key, value)|
44
+ memo[key] = lambda { |output_value| value.merge('value' => output_value) }
45
+ memo
46
+ end
47
+ end
48
+
33
49
  def update_stack(template, params, capabilities)
34
50
  @cf_driver.update_stack(@stack_name, template,
35
51
  :capabilities => capabilities,
@@ -0,0 +1,62 @@
1
+ require 'securerandom'
2
+ require 'digest'
3
+
4
+ module EbDeployer
5
+ class ConfigLoader
6
+
7
+ class EvalBinding
8
+ def initialize(package_digest)
9
+ @package_digest = package_digest
10
+ end
11
+
12
+ def random_hash
13
+ SecureRandom.hex[0..9]
14
+ end
15
+
16
+ def package_digest
17
+ @package_digest
18
+ end
19
+ end
20
+
21
+ def load(options)
22
+ options = options.dup
23
+ package_digest = package_digest(options[:package])
24
+ config_file = options.delete(:config_file)
25
+ config_settings = load_config_settings(config_file, package_digest)
26
+
27
+ common_settings = symbolize_keys(config_settings.delete(:common))
28
+ common_settings[:version_label] ||= package_digest
29
+
30
+ env = options[:environment]
31
+ envs = config_settings.delete(:environments)
32
+ raise 'Environment #{evn} is not defined in #{config_file}' unless envs.has_key?(env)
33
+ env_settings = symbolize_keys(envs[env] || {})
34
+ env_option_settings = env_settings.delete(:option_settings) || []
35
+
36
+ ret = options.merge(config_settings).merge(common_settings).merge(env_settings)
37
+
38
+ ret[:option_settings] ||= []
39
+ ret[:option_settings] += env_option_settings
40
+ ret
41
+ end
42
+
43
+ private
44
+
45
+ def symbolize_keys(hash)
46
+ hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
47
+ end
48
+
49
+ def load_config_settings(config_file, package_digest)
50
+ yaml = ERB.new(File.read(config_file)).result(eval_binding(package_digest))
51
+ symbolize_keys(YAML.load(yaml))
52
+ end
53
+
54
+ def eval_binding(package_digest)
55
+ EvalBinding.new(package_digest).instance_eval { binding }
56
+ end
57
+
58
+ def package_digest(package)
59
+ Digest::MD5.file(package).hexdigest
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,20 @@
1
+ module EbDeployer
2
+ class DefaultConfig
3
+ attr_reader :app_name
4
+
5
+ def initialize(app_name)
6
+ @app_name = app_name
7
+ end
8
+
9
+ def write_to(path)
10
+ FileUtils.mkdir_p(File.dirname(path))
11
+ File.open(path, 'w') { |f| f << ERB.new(File.read(config_template)).result(binding) }
12
+ end
13
+
14
+ private
15
+
16
+ def config_template
17
+ File.expand_path("../default_config.yml", __FILE__)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,109 @@
1
+ # applicaiton 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: 64bit Amazon Linux running Tomcat 7
8
+
9
+ # AWS region to deploy. Default to us-east-1
10
+ # region: us-west-1
11
+
12
+ # There are two deployment strategies: blue-green or inplace-update.
13
+ # Blue green keep two elastic beanstalk environments and always deploy to
14
+ # inactive one, to achive zero downtime. inplace-update strategy
15
+ # will only keep one environment, and update the version inplace on
16
+ # deploy. inplace-update will save resources but will have downtime. Default
17
+ # strategy is blue-green. PS. All old environments need be destroyed when you
18
+ # switching between strategies.
19
+ # strategy: blue-green
20
+
21
+ # If phoenix mode is turn on, it will terminate the old elastic
22
+ # beanstalk environment and recreate on deploy. For blue-green
23
+ # deployment it terminate the inactive environment first then
24
+ # recreate it. This is useful to avoiding configuration drift and
25
+ # accumulating state on the ec2 instances. Also it has the benifit of
26
+ # keeping your ec2 instance system package upto date, because everytime ec2
27
+ # instance boot up from AMI it does a system update. Default is off but we suggest
28
+ # overriden it to on for production environment.
29
+ # phoenix_mode: false
30
+
31
+ # Generating version label for package be deployed. A readable version label will
32
+ # provide better traceablity of your deployment process.
33
+ # By default setting is:
34
+ # version_label: <%%= package_digest %>
35
+ # which means using MD5 digest of the package file. If you deploy using build
36
+ # pipeline tool such as GO, switching to pipline counter is highly suggested to
37
+ # increase the readability. Following example read pipeline counter from environment
38
+ # variable with a fall back to digest for local deployment:
39
+ # version_label: <%%= ENV['GO_PIPELINE_COUNTER'] || package_digest %>
40
+
41
+
42
+ # Smoke test value should be a piece of ruby code with access to single variable
43
+ # "host_name" -- environment DNS name. Smoke test snippet will be evaluated at
44
+ # the end of the deployment for inplace-update deployment. For blue-green
45
+ # deployment it will run after inactive environment update finish and before
46
+ # switching.
47
+ # Defining a smoke test is high recommended for serious usage. The
48
+ # simplest one could just be checking server landing page using curl, e.g.
49
+ # smoke_test: >
50
+ # curl_http_code = "curl -s -o /dev/null -w \"%{http_code}\" http://#{host_name}"
51
+ # Timeout.timeout(600) do
52
+ # while `#{curl_http_code}`.strip != '200'
53
+ # sleep 5
54
+ # end
55
+ # end
56
+
57
+
58
+ # Elastic Beanstalk settings that will apply to the environments you
59
+ # deploying.
60
+ # For all available options take a look at
61
+ # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
62
+ option_settings:
63
+ # Following is an option_settings example which changes EC2 instance type
64
+ # from t1.micro (default) to m1.small. Intances with t1.micro type sometime
65
+ # are not very responsible, so m1.small is suggested for saving time.
66
+ # But if you care about the marginal cost difference you can comment this out to
67
+ # go back to t1.micro.
68
+ - namespace: aws:autoscaling:launchconfiguration
69
+ option_name: InstanceType
70
+ value: m1.small
71
+
72
+ # If resources specified, eb_deployer will use the CloudFormation
73
+ # template you provide to create a default CloudFormation stack with
74
+ # name <application_name>-<env-name> for the environment current
75
+ # deploying. And Outputs of the CloudFormation can be mapped to Elastic Beanstalk
76
+ # options settings.
77
+ # keys:
78
+ # template => CloudFormation template file with JSON format
79
+ # inputs => A Hash, input values for the CloudFormation template
80
+ # outputs => A Hash with key map to your CloudFormation
81
+ # template outputs and value as elastic beanstalk settings namespace and option_name.
82
+ # :capabilities => An array. You need set it to ['CAPABILITY_IAM'] if the
83
+ # template include IAM Instance Profile.
84
+ resources:
85
+ # For example creating a RDS instance for blue green deployment:
86
+ # template: config/my_rds.json
87
+ # inputs:
88
+ # DBPassword: <%%= random_hash %>
89
+ # outputs:
90
+ # RDSPassSecurityGroup:
91
+ # namespace: aws:autoscaling:launchconfiguration
92
+ # option_name: SecurityGroups
93
+ # RDSDatabaseConfig:
94
+ # namespace: aws:elasticbeanstalk:application:environment
95
+ # option_name: databaseConfig
96
+
97
+
98
+ # You can define environment here. Each environment can overriden any common settings
99
+ environments:
100
+ dev:
101
+ # example for overriding common settings
102
+ # strategy: inplace-update
103
+ production:
104
+ option_settings:
105
+ # example for overriding common option_settings: providing least redanduncy
106
+ # in production environment.
107
+ # - namespace: aws:autoscaling:asg
108
+ # option_name: MinSize
109
+ # value: "2"
@@ -34,6 +34,10 @@ module EbDeployer
34
34
  @bs.environment_swap_cname(self.app, self.name, another.name)
35
35
  end
36
36
 
37
+ def log(msg)
38
+ puts "[#{Time.now.utc}][beanstalk:#{@name}] #{msg}"
39
+ end
40
+
37
41
  private
38
42
 
39
43
  def shorten(str, max_length, digest_length=5)
@@ -64,12 +68,8 @@ module EbDeployer
64
68
  end
65
69
 
66
70
  def smoke_test
67
- if smoke = @creation_opts[:smoke_test]
68
- host = @bs.environment_cname(@app, @name)
69
- log("running smoke test for #{host}...")
70
- smoke.call(host)
71
- log("smoke test succeeded.")
72
- end
71
+ host_name = @bs.environment_cname(@app, @name)
72
+ SmokeTest.new(@creation_opts[:smoke_test]).run(host_name, self)
73
73
  end
74
74
 
75
75
  def with_polling_events(terminate_pattern, &block)
@@ -97,12 +97,9 @@ module EbDeployer
97
97
  end
98
98
  end
99
99
 
100
- def log(msg)
101
- puts "[#{Time.now.utc}][beanstalk-#{@name}] #{msg}"
102
- end
103
100
 
104
101
  def log_event(event)
105
- puts "[#{event[:event_date]}][beanstalk-#{@name}] #{event[:message]}"
102
+ puts "[#{event[:event_date]}][beanstalk:#{@name}] #{event[:message]}"
106
103
  end
107
104
  end
108
105
  end
@@ -0,0 +1,23 @@
1
+ module EbDeployer
2
+ class SmokeTest
3
+ def initialize(test_body)
4
+ @test_body = test_body
5
+ end
6
+
7
+ def run(host_name, logger=nil)
8
+ return unless @test_body
9
+ logger.log("running smoke test for #{host_name}...") if logger
10
+
11
+ case @test_body
12
+ when Proc
13
+ @test_body.call(host_name)
14
+ when String
15
+ eval(@test_body, binding)
16
+ else
17
+ raise "smoke test can only be a string to evaluate or a proc object such as lambda"
18
+ end
19
+
20
+ logger.log("smoke test succeeded.") if logger
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module EbDeployer
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/eb_deployer.rb CHANGED
@@ -8,12 +8,17 @@ require "eb_deployer/event_poller"
8
8
  require "eb_deployer/package"
9
9
  require 'eb_deployer/s3_driver'
10
10
  require 'eb_deployer/cloud_formation_driver'
11
+ require 'eb_deployer/config_loader'
12
+ require 'eb_deployer/default_config'
13
+ require 'eb_deployer/smoke_test'
11
14
  require 'digest'
12
15
  require 'set'
13
16
  require 'time'
14
17
  require 'json'
15
18
  require 'timeout'
16
19
  require 'aws-sdk'
20
+ require 'optparse'
21
+ require 'erb'
17
22
 
18
23
  module EbDeployer
19
24
 
@@ -39,6 +44,8 @@ module EbDeployer
39
44
  end
40
45
 
41
46
 
47
+ # #
48
+ # Deploy a package to specfied environments on elastic beanstalk
42
49
  #
43
50
  # Options available:
44
51
  #
@@ -70,7 +77,7 @@ module EbDeployer
70
77
  # The elastic beanstalk solution stack you want to deploy on top of.
71
78
  # Current possible values include:
72
79
  #
73
- # :settings (optional)
80
+ # :option_settings (or :settings) (optional)
74
81
  # Elastic Beanstalk settings that will apply to the environments you
75
82
  # deploying. Value should be array of hash with format such as:
76
83
  # [{
@@ -90,7 +97,7 @@ module EbDeployer
90
97
  # deploying. Value of resources need to be hash with following
91
98
  # keys:
92
99
  # :template => CloudFormation template file with JSON format
93
- # :parameters => A Hash, input values for the CloudFormation
100
+ # :parameters (or :inputs) => A Hash, input values for the CloudFormation
94
101
  # template
95
102
  # :transforms => A Hash with key map to your CloudFormation
96
103
  # template outputs and value as lambda that return a single or array of
@@ -133,7 +140,6 @@ module EbDeployer
133
140
  # }
134
141
  #
135
142
  #
136
- # deploy a package to specfied environments on elastic beanstalk
137
143
  #
138
144
 
139
145
  def self.deploy(opts)
@@ -150,7 +156,7 @@ module EbDeployer
150
156
  env_name = opts[:environment]
151
157
  version_label = opts[:version_label].to_s.strip
152
158
  cname = opts[:cname]
153
- env_settings = opts[:settings] || []
159
+ env_settings = opts[:option_settings] || opts[:settings] || []
154
160
  strategy_name = opts[:strategy] || :blue_green
155
161
  cname_prefix = opts[:cname_prefix] || [app, env_name].join('-')
156
162
  smoke_test = opts[:smoke_test] || Proc.new {}
@@ -186,4 +192,61 @@ module EbDeployer
186
192
  Application.new(app, bs, s3).delete
187
193
  end
188
194
 
195
+ def self.cli
196
+ options = {
197
+ :action => :deploy,
198
+ :environment => 'dev',
199
+ :config_file => 'config/eb_deployer.yml'
200
+ }
201
+
202
+ parser = cli_parser(options)
203
+ parser.parse!
204
+ action = options.delete(:action)
205
+
206
+ unless File.exists?(options[:config_file])
207
+ puts "Generat default configuration one at location #{options[:config_file]}."
208
+ DefaultConfig.new(File.basename(Dir.pwd)).write_to(options[:config_file])
209
+ exit(2)
210
+ end
211
+
212
+ if !options[:package] && action == :deploy
213
+ puts "Missing options: -p (--package)"
214
+ puts parser
215
+ exit(-1)
216
+ end
217
+
218
+
219
+ self.send(action, ConfigLoader.new.load(options))
220
+ end
221
+
222
+ private
223
+
224
+ def self.cli_parser(options)
225
+ OptionParser.new do |opts|
226
+ opts.banner = "Usage: eb_deployer [options]"
227
+ opts.on("-p", "--package [FILE]", "Package to deploy, for example a war file for java application") do |v|
228
+ options[:package] = v
229
+ end
230
+
231
+ opts.on("-e", "--environment [ENV_NAME]", "(Default to 'dev') Environment to operating on, for example dev, staging or production. This must be defined in 'environments' section of the config file") do |v|
232
+ options[:environment] = v
233
+ end
234
+
235
+ opts.on("-c", "--config-file [FILE]", "eb_deployer config file. Default location is config/eb_deployer.yml") do |v|
236
+ options[:config_file] = v
237
+ end
238
+
239
+ opts.on("-d", "--destroy", "Destroy specified environment") do |v|
240
+ action = :destroy
241
+ end
242
+
243
+ opts.on("-v", "--version", "Print current version") do |v|
244
+ puts "eb_deployer v#{VERSION}"
245
+ exit(0)
246
+ end
247
+
248
+ end
249
+ end
250
+
251
+
189
252
  end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class CloudFormationProvisionerTest < Minitest::Test
4
+ def setup
5
+ @cf = CFStub.new
6
+ @provisioner = EbDeployer::CloudFormationProvisioner.new("myresources", @cf)
7
+ @template = sample_file("sample_template.json")
8
+ end
9
+
10
+
11
+ def test_convert_inputs_as_params_to_cf
12
+ @provisioner.provision(:template => @template,
13
+ :inputs => { 'Foo' => 'Bar' })
14
+
15
+ assert_equal({ 'Foo' => 'Bar' }, @cf.stack_config("myresources")[:parameters])
16
+ end
17
+
18
+ def test_transform_to_eb_settings
19
+ settings = @provisioner.provision(:template => @template,
20
+ :outputs => {
21
+ 'S' => {
22
+ 'namespace' => "foo",
23
+ "option_name" => "bar"
24
+ }
25
+ })
26
+ assert_equal [{'namespace' => 'foo', 'option_name' => 'bar', 'value' => 'value of S'}], settings
27
+ end
28
+ end
@@ -0,0 +1,124 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigLoaderTest < Minitest::Test
4
+ def setup
5
+ @loader = EbDeployer::ConfigLoader.new
6
+ @sample_package = sample_file('app-package.war')
7
+ end
8
+
9
+ def test_all_default_cases
10
+ config = @loader.load(generate_input(<<-YAML))
11
+ application: myapp
12
+ common:
13
+ option_settings:
14
+ resources:
15
+ environments:
16
+ dev:
17
+ production:
18
+ YAML
19
+ assert_equal('myapp', config[:application])
20
+ assert_equal(@sample_package, config[:package])
21
+ assert_equal('dev', config[:environment])
22
+ assert_equal(md5_digest(@sample_package), config[:version_label])
23
+ assert_equal([], config[:option_settings])
24
+ assert_equal(nil, config[:resources])
25
+ assert_equal(nil, config[:common])
26
+ end
27
+
28
+ def test_common_settings_get_merge_into_the_config
29
+ config = @loader.load(generate_input(<<-YAML))
30
+ application: myapp
31
+ common:
32
+ strategy: inplace-update
33
+ phoenix_mode: true
34
+ option_settings:
35
+ - namespace: aws:autoscaling:launchconfiguration
36
+ option_name: InstanceType
37
+ value: m1.small
38
+ resources:
39
+ environments:
40
+ dev:
41
+ production:
42
+ YAML
43
+ assert_equal('inplace-update', config[:strategy])
44
+ assert_equal([{'namespace' => 'aws:autoscaling:launchconfiguration',
45
+ 'option_name' => 'InstanceType',
46
+ 'value' => 'm1.small'}], config[:option_settings])
47
+ end
48
+
49
+ def test_eval_random_hash
50
+ yaml = <<-YAML
51
+ application: myapp
52
+ common:
53
+ resources:
54
+ template: config/my_rds.json
55
+ inputs:
56
+ DBPassword: <%= random_hash %>
57
+ environments:
58
+ dev:
59
+ production:
60
+ YAML
61
+ first_time = @loader.load(generate_input(yaml))[:resources]['inputs']['DBPassword']
62
+ second_time = @loader.load(generate_input(yaml))[:resources]['inputs']['DBPassword']
63
+ assert first_time && second_time
64
+ assert first_time != second_time
65
+ end
66
+
67
+ def test_environment_specific_setting_will_override_common_settings
68
+ yaml = <<-YAML
69
+ application: myapp
70
+ common:
71
+ phoenix_mode: true
72
+ environments:
73
+ dev:
74
+ phoenix_mode: false
75
+ production:
76
+ YAML
77
+
78
+ assert !@loader.load(generate_input(yaml, :environment => 'dev'))[:phoenix_mode]
79
+ assert @loader.load(generate_input(yaml, :environment => 'production'))[:phoenix_mode]
80
+ end
81
+
82
+ def test_env_specific_option_settings_will_merge_with_commons
83
+ config = @loader.load(generate_input(<<-YAML, :environment => 'production'))
84
+ application: myapp
85
+ common:
86
+ strategy: inplace-update
87
+ phoenix_mode: true
88
+ option_settings:
89
+ - namespace: aws:autoscaling:launchconfiguration
90
+ option_name: InstanceType
91
+ value: m1.small
92
+ resources:
93
+ environments:
94
+ dev:
95
+ production:
96
+ option_settings:
97
+ - namespace: aws:autoscaling:asg
98
+ option_name: MinSize
99
+ value: "2"
100
+ YAML
101
+ assert_equal([{'namespace' => 'aws:autoscaling:launchconfiguration',
102
+ 'option_name' => 'InstanceType',
103
+ 'value' => 'm1.small'},
104
+ {'namespace' => 'aws:autoscaling:asg',
105
+ 'option_name' => 'MinSize',
106
+ 'value' => "2"}], config[:option_settings])
107
+ end
108
+
109
+ private
110
+
111
+ def md5_digest(file)
112
+ Digest::MD5.file(file).hexdigest
113
+ end
114
+
115
+ def generate_input(config_file_content, overriding={})
116
+ { :environment => 'dev',
117
+ :package => @sample_package,
118
+ :config_file => generate_config(config_file_content)}.merge(overriding)
119
+ end
120
+
121
+ def generate_config(content)
122
+ sample_file('eb_deployer.yml', content)
123
+ end
124
+ end
data/test/deploy_test.rb CHANGED
@@ -1,16 +1,11 @@
1
- $:.unshift(File.expand_path("../../lib", __FILE__))
2
- require 'tempfile'
3
- require 'eb_deployer'
4
- require 'aws_driver_stubs'
5
- require 'minitest/autorun'
1
+ require 'test_helper'
6
2
 
7
3
  class DeployTest < Minitest::Test
8
4
  def setup
9
5
  @eb_driver = EBStub.new
10
6
  @s3_driver = S3Stub.new
11
7
  @cf_driver = CFStub.new
12
- @sample_package = '/tmp/app-package.war'
13
- File.open(@sample_package, 'w') { |f| f << 's' * 100 }
8
+ @sample_package = sample_file('app-package.war')
14
9
  end
15
10
 
16
11
  def test_first_deplyment_create_eb_application
@@ -19,6 +14,17 @@ class DeployTest < Minitest::Test
19
14
  assert @eb_driver.application_exists?('simple')
20
15
  end
21
16
 
17
+ def test_set_option_settings_on_deployment
18
+ redudant = [{:namespace => 'aws:autoscaling:launchconfiguration',
19
+ :option_name => 'MinSize',
20
+ :value => '2' }]
21
+ deploy(:application => 'simple', :environment => "production",
22
+ :option_settings => [redudant])
23
+
24
+ assert_equal [redudant], @eb_driver.environment_settings('simple', eb_envname('simple', 'production'))
25
+
26
+ end
27
+
22
28
  def test_destroy_should_clean_up_eb_application_and_env
23
29
  deploy(:application => 'simple', :environment => "production")
24
30
  destroy(:application => 'simple')
@@ -304,7 +310,4 @@ class DeployTest < Minitest::Test
304
310
  :cf_driver => @cf_driver
305
311
  }
306
312
  end
307
-
308
-
309
-
310
313
  end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class SmokeTestTest < MiniTest::Test
4
+ def test_call_proc_type_smoke_tests
5
+ host_name_in_proc = nil
6
+ EbDeployer::SmokeTest.new(lambda {|v| host_name_in_proc = v }).run("foo")
7
+
8
+ assert_equal 'foo', host_name_in_proc
9
+ end
10
+
11
+ def test_eval_string_type_smoke_test
12
+ $host_name_in_proc = nil
13
+ EbDeployer::SmokeTest.new("$host_name_in_proc=host_name").run("foo")
14
+ assert_equal 'foo', $host_name_in_proc
15
+ end
16
+
17
+ def test_should_raise_if_test_body_raise
18
+ assert_raises(RuntimeError) do
19
+ EbDeployer::SmokeTest.new("raise host_name").run("foo")
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.expand_path("../../lib", __FILE__))
2
+
3
+ require 'tempfile'
4
+ require 'eb_deployer'
5
+ require 'aws_driver_stubs'
6
+ require 'minitest/autorun'
7
+ require 'minitest/pride'
8
+
9
+
10
+ class Minitest::Test
11
+ def sample_file(file_name, content='s' * 100)
12
+ path = File.join('/tmp', file_name)
13
+ File.open(path, 'w') { |f| f << content }
14
+ path
15
+ end
16
+ end
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.1.1
4
+ version: 0.2.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-08-06 00:00:00.000000000 Z
13
+ date: 2013-09-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -48,7 +48,8 @@ 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
51
- executables: []
51
+ executables:
52
+ - eb_deploy
52
53
  extensions: []
53
54
  extra_rdoc_files: []
54
55
  files:
@@ -57,20 +58,29 @@ files:
57
58
  - LICENSE
58
59
  - README.md
59
60
  - Rakefile
61
+ - bin/eb_deploy
60
62
  - eb_deployer.gemspec
61
63
  - lib/eb_deployer.rb
62
64
  - lib/eb_deployer/application.rb
63
65
  - lib/eb_deployer/beanstalk.rb
64
66
  - lib/eb_deployer/cloud_formation_driver.rb
65
67
  - lib/eb_deployer/cloud_formation_provisioner.rb
68
+ - lib/eb_deployer/config_loader.rb
69
+ - lib/eb_deployer/default_config.rb
70
+ - lib/eb_deployer/default_config.yml
66
71
  - lib/eb_deployer/deployment_strategy.rb
67
72
  - lib/eb_deployer/environment.rb
68
73
  - lib/eb_deployer/event_poller.rb
69
74
  - lib/eb_deployer/package.rb
70
75
  - lib/eb_deployer/s3_driver.rb
76
+ - lib/eb_deployer/smoke_test.rb
71
77
  - lib/eb_deployer/version.rb
72
78
  - test/aws_driver_stubs.rb
79
+ - test/cloud_formation_provisioner_test.rb
80
+ - test/config_loader_test.rb
73
81
  - test/deploy_test.rb
82
+ - test/smoke_test_test.rb
83
+ - test/test_helper.rb
74
84
  homepage: https://github.com/ThoughtWorksStudios/eb_deployer
75
85
  licenses: []
76
86
  post_install_message:
@@ -91,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
101
  version: '0'
92
102
  requirements: []
93
103
  rubyforge_project:
94
- rubygems_version: 1.8.24
104
+ rubygems_version: 1.8.25
95
105
  signing_key:
96
106
  specification_version: 3
97
107
  summary: Low friction deployments should be a breeze. Elastic Beanstalk provides a
@@ -99,5 +109,8 @@ summary: Low friction deployments should be a breeze. Elastic Beanstalk provides
99
109
  top to automate the whole flow out of box.
100
110
  test_files:
101
111
  - test/aws_driver_stubs.rb
112
+ - test/cloud_formation_provisioner_test.rb
113
+ - test/config_loader_test.rb
102
114
  - test/deploy_test.rb
103
- has_rdoc:
115
+ - test/smoke_test_test.rb
116
+ - test/test_helper.rb