eb_deployer 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|