eb_deployer 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ 0.4.3
2
+ =====
3
+ * Backoff and retry when AWS::ElasticBeanstalk::Errors::Throttling happens during polling events
4
+
1
5
  0.4.2
2
6
  =====
3
7
  * Allow provding different deploy strategy for different components.
data/lib/eb_deployer.rb CHANGED
@@ -9,6 +9,7 @@ require 'erb'
9
9
  require 'fileutils'
10
10
 
11
11
  require 'eb_deployer/version'
12
+ require 'eb_deployer/utils'
12
13
  require 'eb_deployer/aws_driver'
13
14
  require 'eb_deployer/deployment_strategy'
14
15
  require 'eb_deployer/cloud_formation_provisioner'
@@ -3,6 +3,7 @@ require 'digest'
3
3
 
4
4
  module EbDeployer
5
5
  class ConfigLoader
6
+ include Utils
6
7
 
7
8
  class EvalBinding
8
9
  def initialize(package_digest)
@@ -44,10 +45,6 @@ module EbDeployer
44
45
 
45
46
  private
46
47
 
47
- def symbolize_keys(hash)
48
- hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
49
- end
50
-
51
48
  def load_config_settings(config_file, package_digest)
52
49
  yaml = ERB.new(File.read(config_file)).result(eval_binding(package_digest))
53
50
  symbolize_keys(YAML.load(yaml))
@@ -1,5 +1,7 @@
1
1
  module EbDeployer
2
2
  class Environment
3
+ include Utils
4
+
3
5
  attr_writer :resource_stacks, :settings, :creation_opts, :components, :component_under_deploy, :strategy_name
4
6
  attr_reader :name
5
7
 
@@ -53,10 +55,6 @@ module EbDeployer
53
55
  @components.detect { |c| c.name == name }
54
56
  end
55
57
 
56
- def symbolize_keys(hash)
57
- hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
58
- end
59
-
60
58
  def resource_stack_name
61
59
  "#{app_name}-#{@name}"
62
60
  end
@@ -1,7 +1,9 @@
1
1
  module EbDeployer
2
2
  class EventPoller
3
- def initialize(app, env, beanstalk)
4
- @app, @env, @beanstalk = app, env, beanstalk
3
+ include Utils
4
+
5
+ def initialize(app, env, eb_driver)
6
+ @app, @env, @eb_driver = app, env, eb_driver
5
7
  end
6
8
 
7
9
  def poll(start_time = Time.now, &block)
@@ -16,7 +18,6 @@ module EbDeployer
16
18
  end
17
19
  end
18
20
 
19
-
20
21
  private
21
22
 
22
23
  def digest(event)
@@ -28,15 +29,21 @@ module EbDeployer
28
29
  end
29
30
 
30
31
  def fetch_events(start_time, &block)
31
- events, next_token = @beanstalk.fetch_events(@app, @env, :start_time => start_time.iso8601)
32
+ events, next_token = fetch_events_from_eb(:start_time => start_time.iso8601)
32
33
  yield(events)
33
34
  fetch_next(next_token, &block) if next_token
34
35
  end
35
36
 
36
37
  def fetch_next(next_token, &block)
37
- events, next_token = @beanstalk.fetch_events(@app, @env, :next_token => next_token)
38
+ events, next_token = fetch_events_from_eb(:next_token => next_token)
38
39
  yield(events)
39
40
  fetch_next(next_token, &block) if next_token
40
41
  end
42
+
43
+ def fetch_events_from_eb(options)
44
+ backoff(AWS::ElasticBeanstalk::Errors::Throttling) do
45
+ @eb_driver.fetch_events(@app, @env, options)
46
+ end
47
+ end
41
48
  end
42
49
  end
@@ -0,0 +1,28 @@
1
+ module EbDeployer
2
+ module Utils
3
+ BACKOFF_INITIAL_SLEEP = 1
4
+
5
+ # A util deal with throttling exceptions
6
+ # example:
7
+ # backoff(AWS::EC2::Errors::RequestLimitExceeded) do
8
+ # ...
9
+ # end
10
+ def backoff(error_class, retry_limit=9, &block)
11
+ next_sleep = BACKOFF_INITIAL_SLEEP
12
+ begin
13
+ yield
14
+ rescue error_class
15
+ raise if retry_limit == 0
16
+ sleep(next_sleep)
17
+ next_sleep *= 2
18
+ retry_limit -= 1
19
+ retry
20
+ end
21
+ end
22
+
23
+ # convert top level key in a hash to symbol
24
+ def symbolize_keys(hash)
25
+ hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module EbDeployer
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.3"
3
3
  end
@@ -2,55 +2,27 @@ require 'deploy_test'
2
2
 
3
3
  class BlueGreenDeployTest < DeployTest
4
4
  def test_blue_green_deployment_strategy_should_create_blue_env_on_first_deployment
5
- deploy(:application => 'simple',
6
- :environment => "production",
7
- :strategy => 'blue-green',
8
- :version_label => 42)
9
-
5
+ do_deploy(42)
10
6
  assert @eb.environment_exists?('simple', t('production-a', 'simple'))
11
7
  assert_equal 'simple-production', @eb.environment_cname_prefix('simple', t('production-a', 'simple'))
12
8
  end
13
9
 
14
10
 
15
11
  def test_blue_green_deployment_should_create_green_env_if_blue_exists
16
- deploy(:application => 'simple',
17
- :environment => "production",
18
- :strategy => 'blue-green',
19
- :version_label => 42)
20
-
21
- deploy(:application => 'simple',
22
- :environment => "production",
23
- :strategy => 'blue-green',
24
- :version_label => 43)
25
-
12
+ do_deploy(42)
13
+ do_deploy(43)
26
14
  assert @eb.environment_exists?('simple', t('production-a', 'simple'))
27
15
  assert @eb.environment_exists?('simple', t('production-b', 'simple'))
28
16
  end
29
17
 
30
18
 
31
19
  def test_blue_green_deployment_should_swap_cname_to_make_active_most_recent_updated_env
32
- deploy(:application => 'simple',
33
- :environment => "production",
34
- :strategy => 'blue-green',
35
- :version_label => 42)
36
-
37
- deploy(:application => 'simple',
38
- :environment => "production",
39
- :strategy => 'blue-green',
40
- :version_label => 43)
41
-
20
+ do_deploy(42)
21
+ do_deploy(43)
42
22
  assert_match(/simple-production-inactive/, @eb.environment_cname_prefix('simple', t('production-a', 'simple')))
43
-
44
23
  assert_equal 'simple-production', @eb.environment_cname_prefix('simple', t('production-b', 'simple'))
45
-
46
-
47
- deploy(:application => 'simple',
48
- :environment => "production",
49
- :strategy => 'blue-green',
50
- :version_label => 44)
51
-
24
+ do_deploy(44)
52
25
  assert_match(/simple-production-inactive/, @eb.environment_cname_prefix('simple', t('production-b', 'simple')))
53
-
54
26
  assert_equal 'simple-production', @eb.environment_cname_prefix('simple', t('production-a', 'simple'))
55
27
  end
56
28
 
@@ -59,11 +31,7 @@ class BlueGreenDeployTest < DeployTest
59
31
  smoked_host = []
60
32
  smoke_test = lambda { |host| smoked_host << host }
61
33
  [42, 43, 44].each do |version_label|
62
- deploy(:application => 'simple',
63
- :environment => "production",
64
- :strategy => 'blue-green',
65
- :smoke_test => smoke_test,
66
- :version_label => version_label)
34
+ do_deploy(version_label, :smoke_test => smoke_test)
67
35
  end
68
36
 
69
37
  assert_equal ['simple-production.elasticbeanstalk.com',
@@ -73,42 +41,22 @@ class BlueGreenDeployTest < DeployTest
73
41
 
74
42
 
75
43
  def test_blue_green_deployment_should_delete_and_recreate_inactive_env_if_phoenix_mode_is_enabled
76
- deploy(:application => 'simple',
77
- :environment => "production",
78
- :strategy => 'blue-green',
79
- :version_label => 42,
80
- :phoenix_mode => true)
81
-
82
- deploy(:application => 'simple',
83
- :environment => "production",
84
- :strategy => 'blue-green',
85
- :version_label => 43,
86
- :phoenix_mode => true)
87
-
44
+ do_deploy(42, :phoenix_mode => true)
45
+ do_deploy(43, :phoenix_mode => true)
88
46
  assert_equal [], @eb.environments_been_deleted('simple')
89
47
 
90
48
  inactive_env = t('production-a', 'simple')
91
49
  assert_match(/inactive/, @eb.environment_cname_prefix('simple', inactive_env))
92
50
 
93
-
94
- deploy(:application => 'simple',
95
- :environment => "production",
96
- :strategy => 'blue-green',
97
- :version_label => 44,
98
- :phoenix_mode => true)
99
-
51
+ do_deploy(44, :phoenix_mode => true)
100
52
  assert_equal [inactive_env], @eb.environments_been_deleted('simple')
101
53
 
102
54
  assert_equal 'simple-production', @eb.environment_cname_prefix('simple', inactive_env)
103
55
  end
104
56
 
105
57
  def test_destroy_should_clean_up_env
106
- 2.times do
107
- deploy(:application => 'simple',
108
- :environment => "production",
109
- :strategy => 'blue-green',
110
- :version_label => 44,
111
- :phoenix_mode => true)
58
+ [42, 44].each do |version|
59
+ do_deploy(version)
112
60
  end
113
61
 
114
62
  destroy(:application => 'simple', :environment => 'production')
@@ -117,5 +65,13 @@ class BlueGreenDeployTest < DeployTest
117
65
  end
118
66
 
119
67
 
68
+ private
69
+
70
+ def do_deploy(version_label, options={})
71
+ deploy( {:application => 'simple',
72
+ :environment => "production",
73
+ :strategy => 'blue-green',
74
+ }.merge(options).merge(:version_label => version_label))
75
+ end
120
76
 
121
77
  end
data/test/deploy_test.rb CHANGED
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class DeployTest < MiniTest::Unit::TestCase
4
4
  def setup
5
- @eb = EBStub.new
5
+ @eb = ErrorRaisingWrapper.new(EBStub.new)
6
6
  @s3_driver = S3Stub.new
7
7
  @cf_driver = CFStub.new
8
8
  @sample_package = sample_file('app-package.war')
@@ -79,4 +79,22 @@ class InplaceUpdateDeployTest < DeployTest
79
79
  assert !@eb.environment_exists?('simple', t('production', 'simple'))
80
80
  end
81
81
 
82
+ def test_deploy_should_raise_error_when_constantly_hitting_throttling_error
83
+ throttling_error = AWS::ElasticBeanstalk::Errors::Throttling.new("bang!")
84
+ @eb.set_error(:fetch_events, throttling_error)
85
+ assert_raises(AWS::ElasticBeanstalk::Errors::Throttling) do
86
+ deploy(:application => 'simple', :environment => "production")
87
+ end
88
+ end
89
+
90
+ def test_deploy_should_retry_on_temporary_throttling_error
91
+ throttling_error = AWS::ElasticBeanstalk::Errors::Throttling.new("bang!")
92
+ error_seq = [throttling_error] * 5
93
+ @eb.set_error_generator(:fetch_events) do
94
+ error_seq.pop
95
+ end
96
+ deploy(:application => 'simple', :environment => "production")
97
+ assert @eb.environment_exists?('simple', t('production', 'simple'))
98
+ end
99
+
82
100
  end
data/test/test_helper.rb CHANGED
@@ -6,6 +6,54 @@ require 'aws_driver_stubs'
6
6
  require 'minitest/autorun'
7
7
  require 'minitest/pride'
8
8
 
9
+ def silence_warnings(&block)
10
+ old_verbose, $VERBOSE = $VERBOSE, nil
11
+ yield
12
+ ensure
13
+ $VERBOSE = old_verbose
14
+ end
15
+
16
+ silence_warnings { EbDeployer::Utils::BACKOFF_INITIAL_SLEEP = 0 }
17
+
18
+ class ErrorRaisingWrapper < SimpleDelegator
19
+ def initialize(stub)
20
+ @errors = {}
21
+ super(stub)
22
+ end
23
+
24
+ def set_error(method, error)
25
+ set_error_generator(method) do
26
+ error
27
+ end
28
+ end
29
+
30
+ def set_error_generator(method, &error_gen)
31
+ define_delegate_method(method)
32
+ @errors[method] = Proc.new(&error_gen)
33
+ end
34
+
35
+ private
36
+ def define_delegate_method(method)
37
+ method = method.to_s
38
+ original_method_name = "__#{method}_without_error"
39
+ raise "method #{method} not defined" unless self.respond_to?(method)
40
+ return if self.respond_to?(original_method_name)
41
+
42
+ self.instance_eval <<-CODE
43
+ def #{original_method_name}(*args, &block)
44
+ self.__get_obj__.send(:#{method}, *args, &block)
45
+ end
46
+
47
+ def #{method}(*args, &block)
48
+ if error_gen = @errors[:fetch_events]
49
+ error = error_gen.call
50
+ raise error if error
51
+ end
52
+ super
53
+ end
54
+ CODE
55
+ end
56
+ end
9
57
 
10
58
  class MiniTest::Unit::TestCase
11
59
  def sample_file(file_name, content='s' * 100)
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.4.2
4
+ version: 0.4.3
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: 2014-04-01 00:00:00.000000000 Z
13
+ date: 2014-04-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -69,6 +69,7 @@ files:
69
69
  - lib/eb_deployer/package.rb
70
70
  - lib/eb_deployer/resource_stacks.rb
71
71
  - lib/eb_deployer/smoke_test.rb
72
+ - lib/eb_deployer/utils.rb
72
73
  - lib/eb_deployer/version.rb
73
74
  - lib/eb_deployer/version_cleaner.rb
74
75
  - test/aws_driver_stubs.rb
@@ -99,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
100
  version: '0'
100
101
  segments:
101
102
  - 0
102
- hash: -3697140249092762497
103
+ hash: 567704794056721741
103
104
  required_rubygems_version: !ruby/object:Gem::Requirement
104
105
  none: false
105
106
  requirements:
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  version: '0'
109
110
  segments:
110
111
  - 0
111
- hash: -3697140249092762497
112
+ hash: 567704794056721741
112
113
  requirements: []
113
114
  rubyforge_project:
114
115
  rubygems_version: 1.8.29