eb_deployer 0.4.5 → 0.4.6
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.lock +2 -2
- data/README.md +5 -63
- data/lib/eb_deployer/eb_environment.rb +2 -2
- data/lib/eb_deployer/event_poller.rb +38 -15
- data/lib/eb_deployer/version.rb +1 -1
- data/test/aws_driver_stubs.rb +52 -18
- data/test/eb_environment_test.rb +9 -5
- data/test/event_poller_test.rb +32 -0
- data/test/rails_generators_test.rb +6 -6
- metadata +4 -2
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
eb_deployer (0.4.
|
4
|
+
eb_deployer (0.4.6)
|
5
5
|
aws-sdk (>= 1.33.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -23,7 +23,7 @@ GEM
|
|
23
23
|
activesupport (3.2.18)
|
24
24
|
i18n (~> 0.6, >= 0.6.4)
|
25
25
|
multi_json (~> 1.0)
|
26
|
-
aws-sdk (1.
|
26
|
+
aws-sdk (1.43.1)
|
27
27
|
json (~> 1.4)
|
28
28
|
nokogiri (>= 1.4.4)
|
29
29
|
builder (3.0.4)
|
data/README.md
CHANGED
@@ -5,6 +5,8 @@ Low friction deployments should be a breeze. Elastic Beanstalk provides a great
|
|
5
5
|
|
6
6
|
EbDeployer thus allows you to do continuous delivery on AWS.
|
7
7
|
|
8
|
+
[Introduction to EbDeployer](http://getmingle.io/scaling/2014/06/13/introduction-to-eb-deployer.html)
|
9
|
+
|
8
10
|
## Installation
|
9
11
|
|
10
12
|
$ gem install eb_deployer
|
@@ -88,71 +90,11 @@ Later tutorials coming soon will cover
|
|
88
90
|
|
89
91
|
Take a look at code if you can not wait for the documentation.
|
90
92
|
|
91
|
-
##
|
92
|
-
|
93
|
-
EbDeployer ships with a Rails 3+ generator since version 0.4.5.
|
94
|
-
|
95
|
-
### Install
|
96
|
-
|
97
|
-
Add eb_deployer to your Gemfile
|
98
|
-
|
99
|
-
gem 'eb_deployer'
|
100
|
-
|
101
|
-
Setup AWS credentials:
|
102
|
-
|
103
|
-
$ export AWS_ACCESS_KEY_ID=xxx
|
104
|
-
$ export AWS_SECRET_ACCESS_KEY=xxx
|
105
|
-
|
106
|
-
### Initial configurations and rake tasks
|
107
|
-
|
108
|
-
Run Rails generator to generate configurations and rake file:
|
109
|
-
|
110
|
-
rails generate eb_deployer:install
|
111
|
-
|
112
|
-
It will setup AWS Elastic Beanstalk blue-green deployment configuration with a Postgres RDS instance as your Rails' backend database.
|
113
|
-
The followings are details:
|
114
|
-
|
115
|
-
* Add file "lib/tasks/eb_deployer.rake", please run "rake -T eb" for tasks description. These tasks are simple and designed for you to customize.
|
116
|
-
* Add file "config/eb_deployer.yml", it includes basic blue-green configurations with a Postgres RDS instance resource.
|
117
|
-
* Add file "config/rds.json", it is a CloudFormation template file which provisions the Postgres RDS instance. A separated CloudFormation stack maintains all resources that are shared between different Elastic Beanstalk environments in blue-green deployment. Notice: each eb_deployer environment will create one.
|
118
|
-
* Add "gem 'pg'" to your Gemfile, as this initial configuration is hooking up with a Postgres RDS instance, we need postgres driver.
|
119
|
-
* Add file ".ebextenstions/01_postgres_packages.config", which installs Postgres dev packages on EC2 instances, so that we can build gem "pg" on your EC2 machine after deployed.
|
120
|
-
* Add a new production database configuration into "config/database.yml" file. Your original production configuration will be commented out.
|
121
|
-
|
122
|
-
### Deploy
|
123
|
-
|
124
|
-
Add all files that need to be deployed into your Git repository, because we will simply use "git ls-files" to find all files need to be packaged.
|
125
|
-
|
126
|
-
Deploy a dev environment for testing your application deployment:
|
127
|
-
|
128
|
-
rake eb:deploy
|
129
|
-
|
130
|
-
Then, when you're ready to deploy a production environment:
|
131
|
-
|
132
|
-
EB_DEPLOYER_ENV=production rake eb:deploy
|
133
|
-
|
134
|
-
## EbDeployer environment
|
135
|
-
|
136
|
-
There are so many things called environment:
|
137
|
-
|
138
|
-
* Rails environment: development, test, production
|
139
|
-
* Elastic Beanstalk environment
|
140
|
-
* Development environment
|
141
|
-
* Staging environment
|
142
|
-
* Production environment
|
143
|
-
|
144
|
-
An EbDeployer environment is your application running environment (= your running application + infrastructure), e.g. staging environment, production environment.
|
145
|
-
All EbDeployer environments including dev environment are deployed as Rails production environment.
|
93
|
+
## More
|
146
94
|
|
147
|
-
|
95
|
+
[Rails 3 Support](https://github.com/ThoughtWorksStudios/eb_deployer/wiki/Rails-3-Support)
|
148
96
|
|
149
|
-
|
150
|
-
* EbDeployer environment sits between Elastic Beanstalk Application and Elastic Beanstalk Environment:
|
151
|
-
* One Elastic Beanstalk Application has many EbDeployer environments: dev, staging, production, or whatever names you like.
|
152
|
-
* Depending on deployment strategy, one EbDeployer environment has one or more Elastic Beanstalk environments
|
153
|
-
* For 'inplace-update' deployment strategy, it's one Elastic Beanstalk environment.
|
154
|
-
* For 'blue-green' deployment strategy, it's two Elastic Beanstalk environments.
|
155
|
-
* You should consider an Elastic Beanstalk environment is designed to be replacable (by another Elastic Beanstalk environment).
|
97
|
+
[EbDeployer environment](https://github.com/ThoughtWorksStudios/eb_deployer/wiki/EbDeployer-environment)
|
156
98
|
|
157
99
|
## Contributing
|
158
100
|
|
@@ -89,9 +89,9 @@ module EbDeployer
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def with_polling_events(terminate_pattern, &block)
|
92
|
-
|
92
|
+
anchor = event_poller.get_anchor
|
93
93
|
yield
|
94
|
-
event_poller.poll(
|
94
|
+
event_poller.poll(anchor) do |event|
|
95
95
|
if event[:message] =~ /Failed to deploy application/
|
96
96
|
raise event[:message]
|
97
97
|
end
|
@@ -6,13 +6,35 @@ module EbDeployer
|
|
6
6
|
@app, @env, @eb_driver = app, env, eb_driver
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
9
|
+
def get_anchor
|
10
|
+
events, _ = fetch_events_from_eb(:max_records => 1)
|
11
|
+
events.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def poll(from_anchor, &block)
|
10
15
|
handled = Set.new
|
11
16
|
loop do
|
12
|
-
fetch_events(
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
fetch_events(from_anchor) do |events|
|
18
|
+
# events from api is latest first order
|
19
|
+
to_be_handled = []
|
20
|
+
reached_anchor = false
|
21
|
+
|
22
|
+
events.each do |event|
|
23
|
+
if digest(event) == digest(from_anchor)
|
24
|
+
reached_anchor = true
|
25
|
+
end
|
26
|
+
|
27
|
+
if !handled.include?(digest(event)) && !reached_anchor
|
28
|
+
to_be_handled << event
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
to_be_handled.reverse.each do |event|
|
33
|
+
yield(event)
|
34
|
+
handled << digest(event)
|
35
|
+
end
|
36
|
+
|
37
|
+
!reached_anchor
|
16
38
|
end
|
17
39
|
sleep 15
|
18
40
|
end
|
@@ -21,23 +43,24 @@ module EbDeployer
|
|
21
43
|
private
|
22
44
|
|
23
45
|
def digest(event)
|
46
|
+
return nil unless event
|
24
47
|
JSON.dump(event)
|
25
48
|
end
|
26
49
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
events, next_token = fetch_events_from_eb(
|
33
|
-
yield(events)
|
34
|
-
fetch_next(next_token, &block) if next_token
|
50
|
+
def fetch_events(from_anchor, &block)
|
51
|
+
options = {}
|
52
|
+
if from_anchor && from_anchor[:event_date]
|
53
|
+
options[:start_time] = from_anchor[:event_date].iso8601
|
54
|
+
end
|
55
|
+
events, next_token = fetch_events_from_eb(options)
|
56
|
+
should_continue = yield(events)
|
57
|
+
fetch_next(next_token, &block) if next_token && should_continue
|
35
58
|
end
|
36
59
|
|
37
60
|
def fetch_next(next_token, &block)
|
38
61
|
events, next_token = fetch_events_from_eb(:next_token => next_token)
|
39
|
-
yield(events)
|
40
|
-
fetch_next(next_token, &block) if next_token
|
62
|
+
should_continue = yield(events)
|
63
|
+
fetch_next(next_token, &block) if next_token && should_continue
|
41
64
|
end
|
42
65
|
|
43
66
|
def fetch_events_from_eb(options)
|
data/lib/eb_deployer/version.rb
CHANGED
data/test/aws_driver_stubs.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
class EBStub
|
2
|
-
attr_reader :envs
|
2
|
+
attr_reader :envs, :events
|
3
3
|
def initialize
|
4
4
|
@apps = []
|
5
5
|
@envs = {}
|
6
6
|
@versions = {}
|
7
7
|
@envs_been_deleted = {}
|
8
8
|
@versions_deleted = {}
|
9
|
+
@event_fetched_times = 0
|
9
10
|
end
|
10
11
|
|
11
12
|
def create_application(app)
|
@@ -88,21 +89,53 @@ class EBStub
|
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
92
|
+
# simulating elasticbeanstalk events api behavior
|
93
|
+
# use set_events and append_events to set fake events
|
91
94
|
def fetch_events(app_name, env_name, options={})
|
92
|
-
|
95
|
+
@event_fetched_times += 1
|
96
|
+
set_env_ready(app_name, env_name, true) # assume env become ready after it spit out all the events
|
93
97
|
|
94
98
|
unless @events # unrestricted mode for testing if no explicit events set
|
95
|
-
return generate_event_from_messages(['Environment update completed successfully',
|
99
|
+
return [generate_event_from_messages(['Environment update completed successfully',
|
96
100
|
'terminateEnvironment completed successfully',
|
97
101
|
'Successfully launched environment',
|
98
102
|
'Completed swapping CNAMEs for environments'
|
99
|
-
])
|
103
|
+
], Time.now + @event_fetched_times), nil]
|
100
104
|
end
|
101
105
|
|
102
|
-
@events[env_key(app_name, env_name)]
|
103
|
-
|
104
|
-
|
106
|
+
events = @events[env_key(app_name, env_name)][@event_fetched_times - 1]
|
107
|
+
|
108
|
+
if options.has_key?(:start_time)
|
109
|
+
start_time = Time.parse(options[:start_time])
|
110
|
+
events = events.select { |e| e[:event_date] >= start_time }
|
111
|
+
end
|
112
|
+
|
113
|
+
if limit = options[:max_records]
|
114
|
+
events = events[0..limit]
|
115
|
+
end
|
105
116
|
|
117
|
+
[events, nil]
|
118
|
+
end
|
119
|
+
|
120
|
+
# add fake events for each times of fetch events call
|
121
|
+
# message passed in should be old to new order
|
122
|
+
# e.g. given set_events("myapp", "test", ['a'], ['b', 'c'])
|
123
|
+
# then
|
124
|
+
# fetch_events('myapp', 'test') # => [{message: 'a'}] for first time
|
125
|
+
# fetch_events('myapp', 'test') # => [{message: 'c'}, {message: 'b'}, {message: 'a'}] for the second time call
|
126
|
+
def set_events(app_name, env_name, *messages)
|
127
|
+
events_seq = []
|
128
|
+
messages.each do |messages_for_call_seq|
|
129
|
+
if old_events = events_seq.last
|
130
|
+
last_event_date = old_events.first && old_events.first[:event_date]
|
131
|
+
events_seq << (generate_event_from_messages(messages_for_call_seq, last_event_date) + old_events)
|
132
|
+
else
|
133
|
+
events_seq << generate_event_from_messages(messages_for_call_seq)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
@events ||= {}
|
137
|
+
@events[env_key(app_name, env_name)] = events_seq
|
138
|
+
end
|
106
139
|
|
107
140
|
def environment_cname_prefix(app_name, env_name)
|
108
141
|
return unless @envs[env_key(app_name, env_name)]
|
@@ -135,7 +168,7 @@ class EBStub
|
|
135
168
|
end
|
136
169
|
|
137
170
|
def list_solution_stack_names
|
138
|
-
@solution_stacks || ["64bit Amazon Linux 2014.
|
171
|
+
@solution_stacks || ["64bit Amazon Linux 2014.03 v1.0.3 running Tomcat 7 Java 7"]
|
139
172
|
end
|
140
173
|
|
141
174
|
#test only
|
@@ -175,11 +208,6 @@ class EBStub
|
|
175
208
|
@versions_deleted[app_name]
|
176
209
|
end
|
177
210
|
|
178
|
-
def set_events(app_name, env_name, messages)
|
179
|
-
@events ||= {}
|
180
|
-
@events[env_key(app_name, env_name)] = generate_event_from_messages(messages)
|
181
|
-
end
|
182
|
-
|
183
211
|
private
|
184
212
|
|
185
213
|
def set_env_ready(app, env, ready)
|
@@ -192,11 +220,17 @@ class EBStub
|
|
192
220
|
@envs[env_key(app, env)][:ready]
|
193
221
|
end
|
194
222
|
|
195
|
-
def generate_event_from_messages(messages)
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
223
|
+
def generate_event_from_messages(messages, start_time=Time.now)
|
224
|
+
start_time ||= Time.now
|
225
|
+
events = messages.map do |m|
|
226
|
+
{ :message => m }
|
227
|
+
end
|
228
|
+
|
229
|
+
events.each_with_index do |e, i|
|
230
|
+
e[:event_date] = start_time + (i + 1)
|
231
|
+
end
|
232
|
+
|
233
|
+
events.reverse
|
200
234
|
end
|
201
235
|
|
202
236
|
def env_key(app, name)
|
data/test/eb_environment_test.rb
CHANGED
@@ -43,16 +43,20 @@ class EbEnvironmentTest < MiniTest::Unit::TestCase
|
|
43
43
|
|
44
44
|
def test_should_raise_runtime_error_when_deploy_failed
|
45
45
|
env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
|
46
|
-
@eb_driver.set_events("myapp", t("production", 'myapp'),
|
46
|
+
@eb_driver.set_events("myapp", t("production", 'myapp'),
|
47
|
+
[],
|
48
|
+
["start deploying", "Failed to deploy application"])
|
47
49
|
assert_raises(RuntimeError) { env.deploy("version 1") }
|
48
50
|
end
|
49
51
|
|
50
52
|
def test_should_raise_runtime_error_when_eb_extension_execution_failed
|
51
53
|
env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
|
52
|
-
@eb_driver.set_events("myapp", t("production", 'myapp'),
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
@eb_driver.set_events("myapp", t("production", 'myapp'),
|
55
|
+
[],
|
56
|
+
["start deploying",
|
57
|
+
"create environment",
|
58
|
+
"Command failed on instance. Return code: 1 Output: Error occurred during build: Command hooks failed",
|
59
|
+
"Successfully launched environment"])
|
56
60
|
|
57
61
|
assert_raises(RuntimeError) { env.deploy("version 1") }
|
58
62
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventPollerTest < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@eb = EBStub.new
|
6
|
+
@poller = EbDeployer::EventPoller.new("myapp", "test", @eb)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_run_handle_block_through_all_events_when_there_is_no_from_anchor
|
10
|
+
messages_handled = []
|
11
|
+
@eb.set_events('myapp', 'test', ['a', 'b', nil])
|
12
|
+
@poller.poll(nil) do |event|
|
13
|
+
break if event[:message].nil?
|
14
|
+
messages_handled << event[:message]
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_equal ['a', 'b'], messages_handled
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def test_can_poll_all_events_after_an_anchor
|
22
|
+
@eb.set_events('myapp', 'test', ['a', 'b'], ['c', 'd', nil])
|
23
|
+
anchor = @poller.get_anchor
|
24
|
+
messages_handled = []
|
25
|
+
@poller.poll(anchor) do |event|
|
26
|
+
break if event[:message].nil?
|
27
|
+
messages_handled << event[:message]
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_equal ['c', 'd'], messages_handled
|
31
|
+
end
|
32
|
+
end
|
@@ -22,7 +22,7 @@ class RailsGenratorsTest < Rails::Generators::TestCase
|
|
22
22
|
|
23
23
|
assert_file 'config/rds.json'
|
24
24
|
assert_file '.ebextensions/01_postgres_packages.config'
|
25
|
-
assert_file 'config/database.yml', /database: <%= ENV\['
|
25
|
+
assert_file 'config/database.yml', /database: <%= ENV\['DATABASE_NAME'\]/m, /host: <%= ENV\['DATABASE_HOST'\]/m
|
26
26
|
assert_file 'Gemfile', /gem "pg"/
|
27
27
|
end
|
28
28
|
|
@@ -52,11 +52,11 @@ test:
|
|
52
52
|
|
53
53
|
production:
|
54
54
|
adapter: postgresql
|
55
|
-
database: <%= ENV['
|
56
|
-
host: <%= ENV['
|
57
|
-
port: <%= ENV['
|
58
|
-
username: <%= ENV['
|
59
|
-
password: <%= ENV['
|
55
|
+
database: <%= ENV['DATABASE_NAME'] || 'tmp_production' %>
|
56
|
+
host: <%= ENV['DATABASE_HOST'] || 'localhost' %>
|
57
|
+
port: <%= ENV['DATABASE_PORT'] || 5432 %>
|
58
|
+
username: <%= ENV['DATABASE_USERNAME'] || #{ENV['USER'].inspect} %>
|
59
|
+
password: <%= ENV['DATABASE_PASSWORD'] %>
|
60
60
|
min_messages: ERROR
|
61
61
|
YAML
|
62
62
|
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.4.
|
4
|
+
version: 0.4.6
|
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-06-
|
13
|
+
date: 2014-06-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: aws-sdk
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- test/config_loader_test.rb
|
87
87
|
- test/deploy_test.rb
|
88
88
|
- test/eb_environment_test.rb
|
89
|
+
- test/event_poller_test.rb
|
89
90
|
- test/inplace_update_deploy_test.rb
|
90
91
|
- test/multi_components_deploy_test.rb
|
91
92
|
- test/rails_generators_test.rb
|
@@ -128,6 +129,7 @@ test_files:
|
|
128
129
|
- test/config_loader_test.rb
|
129
130
|
- test/deploy_test.rb
|
130
131
|
- test/eb_environment_test.rb
|
132
|
+
- test/event_poller_test.rb
|
131
133
|
- test/inplace_update_deploy_test.rb
|
132
134
|
- test/multi_components_deploy_test.rb
|
133
135
|
- test/rails_generators_test.rb
|