eb_deployer 0.4.2 → 0.4.3

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.
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