eb_deployer 0.1.0 → 0.1.1
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/Gemfile +4 -0
- data/lib/eb_deployer.rb +29 -1
- data/lib/eb_deployer/deployment_strategy.rb +4 -6
- data/lib/eb_deployer/environment.rb +20 -9
- data/lib/eb_deployer/event_poller.rb +6 -5
- data/lib/eb_deployer/version.rb +1 -1
- data/test/aws_driver_stubs.rb +14 -1
- data/test/deploy_test.rb +38 -2
- metadata +4 -3
data/Gemfile
CHANGED
data/lib/eb_deployer.rb
CHANGED
@@ -16,6 +16,16 @@ require 'timeout'
|
|
16
16
|
require 'aws-sdk'
|
17
17
|
|
18
18
|
module EbDeployer
|
19
|
+
|
20
|
+
##
|
21
|
+
# Query ouput value of the cloud formation stack
|
22
|
+
# arguments:
|
23
|
+
# key: CloudFormation ouput key
|
24
|
+
# options: a hash
|
25
|
+
# :application application name
|
26
|
+
# :environment environment name (e.g. staging, production)
|
27
|
+
#
|
28
|
+
|
19
29
|
def self.query_resource_output(key, opts)
|
20
30
|
# AWS.config(:logger => Logger.new($stdout))
|
21
31
|
if region = opts[:region]
|
@@ -88,6 +98,22 @@ module EbDeployer
|
|
88
98
|
# :capabilities => An array. You need set it to ['CAPABILITY_IAM']
|
89
99
|
# if you want to provision IAM Instance Profile.
|
90
100
|
#
|
101
|
+
# :strategy (optional default :blue-green)
|
102
|
+
# There are two options: blue-green or inplace-update. Blue green
|
103
|
+
# keep two elastic beanstalk environments and always deploy to
|
104
|
+
# inactive one, to achive zero downtime. inplace-update strategy
|
105
|
+
# will only keep one environment, and update the version inplace on
|
106
|
+
# deploy. this will save resources but will have downtime.
|
107
|
+
#
|
108
|
+
# :phoenix_mode (optional default false)
|
109
|
+
# If phoenix mode is turn on, it will terminate the old elastic
|
110
|
+
# beanstalk environment and recreate on deploy. For blue-green
|
111
|
+
# deployment it terminate the inactive environment first then
|
112
|
+
# recreate it. This is useful to avoiding configuration drift and
|
113
|
+
# accumulating state on the ec2 instances. Also it has the benifit of
|
114
|
+
# keeping your ec2 instance system package upto date, because everytime ec2
|
115
|
+
# instance boot up from AMI it does a system update.
|
116
|
+
#
|
91
117
|
# :smoke_test (optional)
|
92
118
|
# Value should be a proc or a lambda which accept single argument that will
|
93
119
|
# passed in as environment DNS name. Smoke test proc or lambda will be
|
@@ -128,6 +154,7 @@ module EbDeployer
|
|
128
154
|
strategy_name = opts[:strategy] || :blue_green
|
129
155
|
cname_prefix = opts[:cname_prefix] || [app, env_name].join('-')
|
130
156
|
smoke_test = opts[:smoke_test] || Proc.new {}
|
157
|
+
phoenix_mode = opts[:phoenix_mode]
|
131
158
|
|
132
159
|
application = Application.new(app, bs, s3)
|
133
160
|
|
@@ -136,7 +163,8 @@ module EbDeployer
|
|
136
163
|
strategy = DeploymentStrategy.create(strategy_name, app, env_name, bs,
|
137
164
|
:solution_stack => stack_name,
|
138
165
|
:cname_prefix => cname_prefix,
|
139
|
-
:smoke_test => smoke_test
|
166
|
+
:smoke_test => smoke_test,
|
167
|
+
:phoenix_mode => phoenix_mode)
|
140
168
|
|
141
169
|
if resources = opts[:resources]
|
142
170
|
env_settings += cf.provision(resources)
|
@@ -19,9 +19,8 @@ module EbDeployer
|
|
19
19
|
@app = app
|
20
20
|
@env_name = env_name
|
21
21
|
@eb_driver = eb_driver
|
22
|
+
@env_creation_opts = env_creation_opts
|
22
23
|
@major_cname_prefix = env_creation_opts[:cname_prefix]
|
23
|
-
@solution_stack = env_creation_opts[:solution_stack]
|
24
|
-
@smoke_test = env_creation_opts[:smoke_test]
|
25
24
|
end
|
26
25
|
|
27
26
|
def deploy(version_label, env_settings)
|
@@ -48,10 +47,9 @@ module EbDeployer
|
|
48
47
|
end
|
49
48
|
|
50
49
|
def env(suffix, cname_prefix=nil)
|
51
|
-
Environment.new(@app, @env_name + '-' + suffix,
|
52
|
-
|
53
|
-
:cname_prefix => cname_prefix || inactive_cname_prefix
|
54
|
-
:smoke_test => @smoke_test)
|
50
|
+
Environment.new(@app, @env_name + '-' + suffix,
|
51
|
+
@eb_driver,
|
52
|
+
@env_creation_opts.merge({:cname_prefix => cname_prefix || inactive_cname_prefix}))
|
55
53
|
end
|
56
54
|
|
57
55
|
def inactive_cname_prefix
|
@@ -13,12 +13,11 @@ module EbDeployer
|
|
13
13
|
@name = self.class.unique_ebenv_name(app, env_name)
|
14
14
|
@bs = eb_driver
|
15
15
|
@creation_opts = creation_opts
|
16
|
-
@poller = EventPoller.new(@app, @name, @bs)
|
17
16
|
end
|
18
17
|
|
19
18
|
def deploy(version_label, settings)
|
19
|
+
terminate if @creation_opts[:phoenix_mode]
|
20
20
|
create_or_update_env(version_label, settings)
|
21
|
-
poll_events
|
22
21
|
smoke_test
|
23
22
|
wait_for_env_become_healthy
|
24
23
|
end
|
@@ -37,7 +36,6 @@ module EbDeployer
|
|
37
36
|
|
38
37
|
private
|
39
38
|
|
40
|
-
|
41
39
|
def shorten(str, max_length, digest_length=5)
|
42
40
|
raise "max length (#{max_length}) should be larger than digest_length (#{digest_length})" if max_length < digest_length
|
43
41
|
return self if str.size <= max_length
|
@@ -45,11 +43,23 @@ module EbDeployer
|
|
45
43
|
sha1[0..(digest_length - 1)] + str[(max_length - digest_length - 1)..-1]
|
46
44
|
end
|
47
45
|
|
46
|
+
def terminate
|
47
|
+
if @bs.environment_exists?(@app, @name)
|
48
|
+
with_polling_events(/terminateEnvironment completed successfully/i) do
|
49
|
+
@bs.delete_environment(@app, @name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
48
54
|
def create_or_update_env(version_label, settings)
|
49
55
|
if @bs.environment_exists?(@app, @name)
|
50
|
-
|
56
|
+
with_polling_events(/Environment update completed successfully/i) do
|
57
|
+
@bs.update_environment(@app, @name, version_label, settings)
|
58
|
+
end
|
51
59
|
else
|
52
|
-
|
60
|
+
with_polling_events(/Successfully launched environment/i) do
|
61
|
+
@bs.create_environment(@app, @name, @creation_opts[:solution_stack], @creation_opts[:cname_prefix], version_label, settings)
|
62
|
+
end
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
@@ -62,13 +72,14 @@ module EbDeployer
|
|
62
72
|
end
|
63
73
|
end
|
64
74
|
|
65
|
-
def
|
66
|
-
|
75
|
+
def with_polling_events(terminate_pattern, &block)
|
76
|
+
event_start_time = Time.now
|
77
|
+
yield
|
78
|
+
EventPoller.new(@app, @name, @bs).poll(event_start_time) do |event|
|
67
79
|
raise event[:message] if event[:message] =~ /Failed to deploy application/
|
68
80
|
|
69
81
|
log_event(event)
|
70
|
-
break if event[:message] =~
|
71
|
-
event[:message] =~ /Successfully launched environment/
|
82
|
+
break if event[:message] =~ terminate_pattern
|
72
83
|
end
|
73
84
|
end
|
74
85
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module EbDeployer
|
2
2
|
class EventPoller
|
3
3
|
def initialize(app, env, beanstalk)
|
4
|
-
@app, @env, @beanstalk
|
4
|
+
@app, @env, @beanstalk = app, env, beanstalk
|
5
5
|
end
|
6
6
|
|
7
|
-
def poll(&block)
|
7
|
+
def poll(start_time = Time.now, &block)
|
8
8
|
handled = Set.new
|
9
9
|
loop do
|
10
|
-
fetch_events do |events|
|
10
|
+
fetch_events(start_time) do |events|
|
11
11
|
new_events = events.reject { |e| handled.include?(digest(e)) }
|
12
12
|
handle(new_events, &block)
|
13
13
|
handled += new_events.map { |e| digest(e) }
|
@@ -16,6 +16,7 @@ module EbDeployer
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
|
19
20
|
private
|
20
21
|
|
21
22
|
def digest(event)
|
@@ -26,8 +27,8 @@ module EbDeployer
|
|
26
27
|
events.reverse.each(&block)
|
27
28
|
end
|
28
29
|
|
29
|
-
def fetch_events(&block)
|
30
|
-
events, next_token = @beanstalk.fetch_events(@app, @env, :start_time =>
|
30
|
+
def fetch_events(start_time, &block)
|
31
|
+
events, next_token = @beanstalk.fetch_events(@app, @env, :start_time => start_time.iso8601)
|
31
32
|
yield(events)
|
32
33
|
fetch_next(next_token, &block) if next_token
|
33
34
|
end
|
data/lib/eb_deployer/version.rb
CHANGED
data/test/aws_driver_stubs.rb
CHANGED
@@ -3,6 +3,7 @@ class EBStub
|
|
3
3
|
@apps = []
|
4
4
|
@envs = {}
|
5
5
|
@versions = {}
|
6
|
+
@envs_been_deleted = {}
|
6
7
|
end
|
7
8
|
|
8
9
|
def create_application(app)
|
@@ -33,6 +34,8 @@ class EBStub
|
|
33
34
|
|
34
35
|
def delete_environment(app, env)
|
35
36
|
@envs.delete(env)
|
37
|
+
@envs_been_deleted[app] ||= []
|
38
|
+
@envs_been_deleted[app] << env
|
36
39
|
end
|
37
40
|
|
38
41
|
def update_environment(app, env, version, settings)
|
@@ -54,7 +57,12 @@ class EBStub
|
|
54
57
|
|
55
58
|
def fetch_events(app_name, env_name, options={})
|
56
59
|
[[{:event_date => Time.now.utc,
|
57
|
-
:message => 'Environment update completed successfully'}
|
60
|
+
:message => 'Environment update completed successfully'},
|
61
|
+
{:event_date => Time.now.utc,
|
62
|
+
:message => 'terminateEnvironment completed successfully'},
|
63
|
+
{:event_date => Time.now.utc,
|
64
|
+
:message => 'Successfully launched environment'}
|
65
|
+
],
|
58
66
|
nil]
|
59
67
|
end
|
60
68
|
|
@@ -80,6 +88,7 @@ class EBStub
|
|
80
88
|
'Green'
|
81
89
|
end
|
82
90
|
|
91
|
+
|
83
92
|
#test only
|
84
93
|
def environment_verion_label(app_name, env_name)
|
85
94
|
@envs[env_key(app_name, env_name)][:version]
|
@@ -97,6 +106,10 @@ class EBStub
|
|
97
106
|
end
|
98
107
|
end
|
99
108
|
|
109
|
+
def environments_been_deleted(app)
|
110
|
+
@envs_been_deleted[app] || []
|
111
|
+
end
|
112
|
+
|
100
113
|
private
|
101
114
|
|
102
115
|
def env_key(app, name)
|
data/test/deploy_test.rb
CHANGED
@@ -88,8 +88,6 @@ class DeployTest < Minitest::Test
|
|
88
88
|
assert_equal 'foobar.elasticbeanstalk.com', host_for_smoke_test
|
89
89
|
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
91
|
def test_blue_green_deployment_strategy_should_create_blue_env_on_first_deployment
|
94
92
|
deploy(:application => 'simple',
|
95
93
|
:environment => "production",
|
@@ -233,6 +231,44 @@ class DeployTest < Minitest::Test
|
|
233
231
|
end
|
234
232
|
end
|
235
233
|
|
234
|
+
def test_should_terminate_old_environment_if_phoenix_mode_is_enabled
|
235
|
+
deploy(:application => 'simple', :environment => "production", :phoenix_mode => true)
|
236
|
+
assert @eb_driver.environment_exists?('simple', eb_envname('simple', 'production'))
|
237
|
+
deploy(:application => 'simple', :environment => "production", :phoenix_mode => true)
|
238
|
+
assert @eb_driver.environments_been_deleted('simple').include?(eb_envname('simple', 'production'))
|
239
|
+
assert @eb_driver.environment_exists?('simple', eb_envname('simple', 'production'))
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_blue_green_deployment_should_delete_and_recreate_inactive_env_if_phoenix_mode_is_enabled
|
243
|
+
deploy(:application => 'simple',
|
244
|
+
:environment => "production",
|
245
|
+
:strategy => 'blue_green',
|
246
|
+
:version_label => 42,
|
247
|
+
:phoenix_mode => true)
|
248
|
+
|
249
|
+
deploy(:application => 'simple',
|
250
|
+
:environment => "production",
|
251
|
+
:strategy => 'blue_green',
|
252
|
+
:version_label => 43,
|
253
|
+
:phoenix_mode => true)
|
254
|
+
|
255
|
+
assert_equal [], @eb_driver.environments_been_deleted('simple')
|
256
|
+
|
257
|
+
inactive_env = eb_envname('simple', 'production-a')
|
258
|
+
assert_match(/inactive/, @eb_driver.environment_cname_prefix('simple', inactive_env))
|
259
|
+
|
260
|
+
|
261
|
+
deploy(:application => 'simple',
|
262
|
+
:environment => "production",
|
263
|
+
:strategy => 'blue_green',
|
264
|
+
:version_label => 44,
|
265
|
+
:phoenix_mode => true)
|
266
|
+
|
267
|
+
assert_equal [inactive_env], @eb_driver.environments_been_deleted('simple')
|
268
|
+
|
269
|
+
assert_equal 'simple-production', @eb_driver.environment_cname_prefix('simple', inactive_env)
|
270
|
+
end
|
271
|
+
|
236
272
|
private
|
237
273
|
|
238
274
|
def temp_file(content)
|
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.
|
4
|
+
version: 0.1.1
|
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-
|
13
|
+
date: 2013-08-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: aws-sdk
|
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
91
|
version: '0'
|
92
92
|
requirements: []
|
93
93
|
rubyforge_project:
|
94
|
-
rubygems_version: 1.8.
|
94
|
+
rubygems_version: 1.8.24
|
95
95
|
signing_key:
|
96
96
|
specification_version: 3
|
97
97
|
summary: Low friction deployments should be a breeze. Elastic Beanstalk provides a
|
@@ -100,3 +100,4 @@ summary: Low friction deployments should be a breeze. Elastic Beanstalk provides
|
|
100
100
|
test_files:
|
101
101
|
- test/aws_driver_stubs.rb
|
102
102
|
- test/deploy_test.rb
|
103
|
+
has_rdoc:
|