stemcell 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +14 -12
- data/examples/stemcellrc +0 -1
- data/lib/stemcell/errors.rb +16 -0
- data/lib/stemcell/launcher.rb +76 -26
- data/lib/stemcell/templates/bootstrap.sh.erb +7 -0
- data/lib/stemcell/version.rb +1 -1
- data/spec/lib/stemcell/launcher_spec.rb +58 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ZDVkYmQ1YzViMzJlNjhmZDBlNGM2YTVmYTZmNjFmM2U3N2VhYjdhZQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 72fabe0d6ead6f78ac3e45619a500e6896e0c6c9
|
4
|
+
data.tar.gz: b53a9302344e97be08143b8ea814d5f2e5a3f092
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
YjdkYWIzNTBkMzY3N2E1ZjE1ZTVlY2U3MjJjODJmNjg1ZGQxMGMyOTYwOGRk
|
11
|
-
ZGQ1OWI2ZDI1ZGY4OTM1MjkwZTlkMDhkOWIyZTE3OTU4NGU4ZTc=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MDYwMzllODg3OTYxN2I5NGE1MWE3ZWE4ODliN2NiMmJkMGM4YjBlZGQ2ZDVm
|
14
|
-
MTU0ZjQwZGQ0NjNjNTNhMWRjN2Y1ZDM2M2IyNDdkNWU4NDdmYjJhOGZjNWI5
|
15
|
-
YTk1MDYyZjUyZjVkOWJhMDBjMGQ5ODA4MTBlYmQxYTgwMTJhNGI=
|
6
|
+
metadata.gz: 06803bd29174493c9127e88575c7847d5782ac2fd77c26687c92d5eaaf3254f882abfc1948fecaa07ccaa77837183b6872bbbef6b3ed2eeda10ebce279f508ad
|
7
|
+
data.tar.gz: cc936abe49a4c3690bb38700c6d70877a6e68bf632a55d4cce732cca48a707b7524db8a546f3279391d53d68e1c532107285a975eedb6e302917915595bec305
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
# next release
|
3
3
|
- ...
|
4
4
|
|
5
|
+
# 0.8.1
|
6
|
+
- Add retry mechanism for instances launch/termination
|
7
|
+
- Make `Launcher::launch!` transaction-like, which reclaims all partially launched instances in the event of non-intermittent error
|
8
|
+
- Display better error message with reason and failed instances
|
9
|
+
- Take converge lock during initial converge
|
10
|
+
|
5
11
|
# 0.8.0
|
6
12
|
- Support for VPC [Brenden](https://github.com/brndnmtthws)
|
7
13
|
- Support relative paths and home alias in `Launcher#try_file` [Patrick Viet](https://github.com/patrickviet)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
stemcell (0.8.
|
4
|
+
stemcell (0.8.1)
|
5
5
|
aws-creds (~> 0.2.2)
|
6
6
|
aws-sdk (~> 1.9)
|
7
7
|
chef (>= 11.4.0)
|
@@ -15,7 +15,9 @@ GEM
|
|
15
15
|
specs:
|
16
16
|
aws-creds (0.2.3)
|
17
17
|
trollop (~> 2.0)
|
18
|
-
aws-sdk (1.
|
18
|
+
aws-sdk (1.62.0)
|
19
|
+
aws-sdk-v1 (= 1.62.0)
|
20
|
+
aws-sdk-v1 (1.62.0)
|
19
21
|
json (~> 1.4)
|
20
22
|
nokogiri (>= 1.4.4)
|
21
23
|
chef (11.14.6)
|
@@ -46,18 +48,18 @@ GEM
|
|
46
48
|
diff-lcs (1.2.5)
|
47
49
|
docile (1.1.5)
|
48
50
|
erubis (2.7.0)
|
49
|
-
ffi (1.9.
|
50
|
-
ffi-yajl (1.
|
51
|
+
ffi (1.9.6)
|
52
|
+
ffi-yajl (1.3.1)
|
51
53
|
ffi (~> 1.5)
|
52
|
-
libyajl2 (~> 1.
|
54
|
+
libyajl2 (~> 1.2)
|
53
55
|
hashie (2.1.2)
|
54
56
|
highline (1.6.21)
|
55
57
|
ipaddress (0.8.0)
|
56
58
|
json (1.7.7)
|
57
|
-
libyajl2 (1.0
|
59
|
+
libyajl2 (1.2.0)
|
58
60
|
method_source (0.8.2)
|
59
61
|
mime-types (1.25.1)
|
60
|
-
mini_portile (0.6.
|
62
|
+
mini_portile (0.6.2)
|
61
63
|
mixlib-authentication (1.3.0)
|
62
64
|
mixlib-log
|
63
65
|
mixlib-cli (1.5.0)
|
@@ -65,14 +67,14 @@ GEM
|
|
65
67
|
mixlib-log (1.6.0)
|
66
68
|
mixlib-shellout (1.4.0)
|
67
69
|
multi_json (1.10.1)
|
68
|
-
net-ssh (2.9.
|
70
|
+
net-ssh (2.9.2)
|
69
71
|
net-ssh-gateway (1.2.0)
|
70
72
|
net-ssh (>= 2.6.5)
|
71
73
|
net-ssh-multi (1.2.0)
|
72
74
|
net-ssh (>= 2.6.5)
|
73
75
|
net-ssh-gateway (>= 1.2.0)
|
74
|
-
nokogiri (1.6.
|
75
|
-
mini_portile (
|
76
|
+
nokogiri (1.6.6.2)
|
77
|
+
mini_portile (~> 0.6.0)
|
76
78
|
ohai (7.2.4)
|
77
79
|
ffi (~> 1.9)
|
78
80
|
ffi-yajl (~> 1.0)
|
@@ -89,7 +91,7 @@ GEM
|
|
89
91
|
coderay (~> 1.1.0)
|
90
92
|
method_source (~> 0.8.1)
|
91
93
|
slop (~> 3.4)
|
92
|
-
rack (1.
|
94
|
+
rack (1.6.0)
|
93
95
|
rake (10.3.2)
|
94
96
|
rest-client (1.6.7)
|
95
97
|
mime-types (>= 1.16)
|
@@ -110,7 +112,7 @@ GEM
|
|
110
112
|
simplecov-html (0.8.0)
|
111
113
|
slop (3.6.0)
|
112
114
|
systemu (2.6.4)
|
113
|
-
trollop (2.
|
115
|
+
trollop (2.1.1)
|
114
116
|
wmi-lite (1.0.0)
|
115
117
|
|
116
118
|
PLATFORMS
|
data/examples/stemcellrc
CHANGED
data/lib/stemcell/errors.rb
CHANGED
@@ -23,4 +23,20 @@ module Stemcell
|
|
23
23
|
@option = option
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
class IncompleteOperation < Error
|
28
|
+
attr_reader :operation, :all_instance_ids, :errors
|
29
|
+
def initialize(operation, all_instance_ids, errors)
|
30
|
+
super()
|
31
|
+
@operation = operation
|
32
|
+
@all_instance_ids = all_instance_ids
|
33
|
+
@errors = errors
|
34
|
+
end
|
35
|
+
|
36
|
+
def message
|
37
|
+
"Incomplete operation '#{@operation}': " +
|
38
|
+
"all_instance_ids=#{@all_instance_ids.join('|')}; " +
|
39
|
+
"errors=" + (@errors.map { |k, v| "'#{k}' => '#{v}'"}.join('|'))
|
40
|
+
end
|
41
|
+
end
|
26
42
|
end
|
data/lib/stemcell/launcher.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'aws-sdk'
|
2
2
|
require 'logger'
|
3
3
|
require 'erb'
|
4
|
+
require 'set'
|
4
5
|
|
5
6
|
require "stemcell/version"
|
6
7
|
require "stemcell/option_parser"
|
@@ -176,8 +177,6 @@ module Stemcell
|
|
176
177
|
end
|
177
178
|
end
|
178
179
|
|
179
|
-
#
|
180
|
-
|
181
180
|
# generate user data script to bootstrap instance, include in launch
|
182
181
|
# options UNLESS we have manually set the user-data (ie. for ec2admin)
|
183
182
|
launch_options[:user_data] = opts.fetch('user_data', render_template(opts))
|
@@ -186,14 +185,24 @@ module Stemcell
|
|
186
185
|
instances = do_launch(launch_options)
|
187
186
|
|
188
187
|
# set tags on all instances launched
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
wait
|
195
|
-
|
196
|
-
|
188
|
+
begin
|
189
|
+
set_tags(instances, tags)
|
190
|
+
@log.info "sent ec2 api requests successfully"
|
191
|
+
|
192
|
+
# wait for aws to report instance stats
|
193
|
+
if opts.fetch('wait', true)
|
194
|
+
wait(instances)
|
195
|
+
print_run_info(instances)
|
196
|
+
@log.info "launched instances successfully"
|
197
|
+
end
|
198
|
+
rescue => e
|
199
|
+
@log.info "launch failed, killing all launched instances"
|
200
|
+
begin
|
201
|
+
kill(instances, :ignore_not_found => true)
|
202
|
+
rescue => kill_error
|
203
|
+
@log.warn "encountered an error during cleanup: #{kill_error.message}"
|
204
|
+
end
|
205
|
+
raise e
|
197
206
|
end
|
198
207
|
|
199
208
|
return instances
|
@@ -203,17 +212,20 @@ module Stemcell
|
|
203
212
|
return @ec2.instances[id]
|
204
213
|
end
|
205
214
|
|
206
|
-
def kill(
|
207
|
-
return if
|
208
|
-
|
215
|
+
def kill(instance_ids, opts={})
|
216
|
+
return if instance_ids.nil?
|
217
|
+
|
218
|
+
errors = run_batch_operation(instance_ids) do |id|
|
209
219
|
begin
|
210
|
-
instance = find_instance(
|
220
|
+
instance = find_instance(id)
|
211
221
|
@log.warn "Terminating instance #{instance.instance_id}"
|
212
222
|
instance.terminate
|
223
|
+
nil # nil == success
|
213
224
|
rescue AWS::EC2::Errors::InvalidInstanceID::NotFound => e
|
214
|
-
|
225
|
+
opts[:ignore_not_found] ? nil : e
|
215
226
|
end
|
216
227
|
end
|
228
|
+
check_errors(:kill, instance_ids, errors)
|
217
229
|
end
|
218
230
|
|
219
231
|
# this is made public for ec2admin usage
|
@@ -243,15 +255,12 @@ module Stemcell
|
|
243
255
|
@log.info "Waiting up to #{@timeout} seconds for #{instances.count} " \
|
244
256
|
"instance(s) (#{instances.inspect}):"
|
245
257
|
|
246
|
-
while
|
247
|
-
|
248
|
-
if
|
249
|
-
kill(instances)
|
258
|
+
while !instances.all? { |i| i.status == :running }
|
259
|
+
elapsed = Time.now - @start_time
|
260
|
+
if elapsed >= @timeout
|
250
261
|
raise TimeoutError, "exceded timeout of #{@timeout}"
|
251
|
-
|
252
|
-
|
253
|
-
if instances.select{|i| i.status != :running }.empty?
|
254
|
-
break
|
262
|
+
else
|
263
|
+
sleep min(5, @timeout - elapsed)
|
255
264
|
end
|
256
265
|
end
|
257
266
|
|
@@ -279,11 +288,17 @@ module Stemcell
|
|
279
288
|
return instances
|
280
289
|
end
|
281
290
|
|
282
|
-
def set_tags(instances=[],tags)
|
291
|
+
def set_tags(instances=[], tags)
|
283
292
|
@log.info "setting tags on instance(s)"
|
284
|
-
instances
|
285
|
-
|
293
|
+
errors = run_batch_operation(instances) do |instance|
|
294
|
+
begin
|
295
|
+
instance.tags.set(tags)
|
296
|
+
nil # nil == success
|
297
|
+
rescue AWS::EC2::Errors::InvalidInstanceID::NotFound => e
|
298
|
+
e
|
299
|
+
end
|
286
300
|
end
|
301
|
+
check_errors(:set_tags, instances.map(&:id), errors)
|
287
302
|
end
|
288
303
|
|
289
304
|
# attempt to accept keys as file paths
|
@@ -291,5 +306,40 @@ module Stemcell
|
|
291
306
|
File.read(File.expand_path(opt)) rescue opt
|
292
307
|
end
|
293
308
|
|
309
|
+
MAX_ATTEMPTS = 3
|
310
|
+
INITIAL_RETRY_SEC = 1
|
311
|
+
|
312
|
+
# Return a Hash of instance => error. Empty hash indicates "no error"
|
313
|
+
# for code block:
|
314
|
+
# - if block returns nil, success
|
315
|
+
# - if block returns non-nil value (e.g., exception), retry 3 times w/ backoff
|
316
|
+
# - if block raises exception, fail
|
317
|
+
def run_batch_operation(instances)
|
318
|
+
instances.map do |instance|
|
319
|
+
begin
|
320
|
+
attempt = 0
|
321
|
+
result = nil
|
322
|
+
while attempt < MAX_ATTEMPTS
|
323
|
+
# sleep idempotently except for the first attempt
|
324
|
+
sleep(INITIAL_RETRY_SEC * 2 ** attempt) if attempt != 0
|
325
|
+
result = yield(instance)
|
326
|
+
break if result.nil? # nil indicates success
|
327
|
+
attempt += 1
|
328
|
+
end
|
329
|
+
result # result for this instance is nil or returned exception
|
330
|
+
rescue => e
|
331
|
+
e # result for this instance is caught exception
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def check_errors(operation, instance_ids, errors)
|
337
|
+
return if errors.all?(&:nil?)
|
338
|
+
raise IncompleteOperation.new(
|
339
|
+
operation,
|
340
|
+
instance_ids,
|
341
|
+
instance_ids.zip(errors).reject { |i, e| e.nil? }
|
342
|
+
)
|
343
|
+
end
|
294
344
|
end
|
295
345
|
end
|
@@ -8,6 +8,11 @@
|
|
8
8
|
# Martin Rhoads
|
9
9
|
|
10
10
|
|
11
|
+
(
|
12
|
+
echo 'Acquiring converge lock...'
|
13
|
+
/usr/bin/flock -e 200
|
14
|
+
echo 'Lock acquired!'
|
15
|
+
|
11
16
|
set -o pipefail
|
12
17
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
13
18
|
|
@@ -146,6 +151,7 @@ repo_dir = "${repo_dir}"
|
|
146
151
|
cookbook_path "#{repo_dir}/cookbooks"
|
147
152
|
role_path "#{repo_dir}/roles"
|
148
153
|
data_bag_path "#{repo_dir}/data_bags"
|
154
|
+
ssl_verify_mode :verify_peer
|
149
155
|
|
150
156
|
log_level :info
|
151
157
|
log_location STDOUT
|
@@ -252,3 +258,4 @@ configure_chef_daemon
|
|
252
258
|
|
253
259
|
|
254
260
|
echo "<%= last_bootstrap_line %>"
|
261
|
+
) 200> /var/run/converge.lock
|
data/lib/stemcell/version.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MockInstance
|
4
|
+
def initialize(id)
|
5
|
+
@id = id
|
6
|
+
end
|
7
|
+
|
8
|
+
def id
|
9
|
+
@id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class MockException < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Stemcell::Launcher do
|
17
|
+
let(:launcher) {
|
18
|
+
opts = {}
|
19
|
+
Stemcell::Launcher::REQUIRED_OPTIONS.map { |k| opts[k] = "" }
|
20
|
+
launcher = Stemcell::Launcher.new(opts)
|
21
|
+
launcher
|
22
|
+
}
|
23
|
+
let(:operation) { 'op' }
|
24
|
+
let(:instances) { (1..4).map { |id| MockInstance.new(id) } }
|
25
|
+
let(:instance_ids) { instances.map(&:id) }
|
26
|
+
|
27
|
+
describe '#run_batch_operation' do
|
28
|
+
|
29
|
+
it "raises no exception when no internal error occur" do
|
30
|
+
errors = launcher.send(:run_batch_operation, instances) {}
|
31
|
+
errors.all?(&:nil?).should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "runs full batch even when there are two error" do
|
35
|
+
errors = launcher.send(:run_batch_operation,
|
36
|
+
instances) do |instance, error|
|
37
|
+
raise "error-#{instance.id}" if instance.id % 2 == 0
|
38
|
+
end
|
39
|
+
errors.count(&:nil?).should be_eql(2)
|
40
|
+
errors.reject(&:nil?).map { |e| e.message }.should \
|
41
|
+
be_eql([2, 4].map { |id| "error-#{id}" })
|
42
|
+
end
|
43
|
+
|
44
|
+
it "retries after an intermittent error" do
|
45
|
+
count = 0
|
46
|
+
errors = launcher.send(:run_batch_operation,
|
47
|
+
instances) do |instance|
|
48
|
+
if instance.id == 3
|
49
|
+
count += 1
|
50
|
+
count < 3 ?
|
51
|
+
AWS::EC2::Errors::InvalidInstanceID::NotFound.new("error-#{instance.id}"):
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
errors.all?(&:nil?).should be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stemcell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Rhoads
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2015-02-18 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: aws-sdk
|
@@ -45,14 +45,14 @@ dependencies:
|
|
45
45
|
name: chef
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
|
-
- -
|
48
|
+
- - '>='
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: 11.4.0
|
51
51
|
type: :runtime
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
|
-
- -
|
55
|
+
- - '>='
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: 11.4.0
|
58
58
|
- !ruby/object:Gem::Dependency
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- spec/fixtures/chef_repo/stemcell-backing-store-missing.json
|
167
167
|
- spec/fixtures/chef_repo/stemcell-defaults-missing.json
|
168
168
|
- spec/fixtures/chef_repo/stemcell.json
|
169
|
+
- spec/lib/stemcell/launcher_spec.rb
|
169
170
|
- spec/lib/stemcell/metadata_source/chef_repository_spec.rb
|
170
171
|
- spec/lib/stemcell/metadata_source/configuration_spec.rb
|
171
172
|
- spec/lib/stemcell/metadata_source_spec.rb
|
@@ -182,17 +183,17 @@ require_paths:
|
|
182
183
|
- lib
|
183
184
|
required_ruby_version: !ruby/object:Gem::Requirement
|
184
185
|
requirements:
|
185
|
-
- -
|
186
|
+
- - '>='
|
186
187
|
- !ruby/object:Gem::Version
|
187
188
|
version: '0'
|
188
189
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
190
|
requirements:
|
190
|
-
- -
|
191
|
+
- - '>='
|
191
192
|
- !ruby/object:Gem::Version
|
192
193
|
version: '0'
|
193
194
|
requirements: []
|
194
195
|
rubyforge_project:
|
195
|
-
rubygems_version: 2.
|
196
|
+
rubygems_version: 2.0.14
|
196
197
|
signing_key:
|
197
198
|
specification_version: 4
|
198
199
|
summary: no summary
|
@@ -218,6 +219,7 @@ test_files:
|
|
218
219
|
- spec/fixtures/chef_repo/stemcell-backing-store-missing.json
|
219
220
|
- spec/fixtures/chef_repo/stemcell-defaults-missing.json
|
220
221
|
- spec/fixtures/chef_repo/stemcell.json
|
222
|
+
- spec/lib/stemcell/launcher_spec.rb
|
221
223
|
- spec/lib/stemcell/metadata_source/chef_repository_spec.rb
|
222
224
|
- spec/lib/stemcell/metadata_source/configuration_spec.rb
|
223
225
|
- spec/lib/stemcell/metadata_source_spec.rb
|