stemcell 0.8.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- Yjk5MmIxNTA2OTg1ZTI1YzVlOTc5NTM3ODFiMjlmNzNmNjdkMjJhMQ==
5
- data.tar.gz: !binary |-
6
- ZDVkYmQ1YzViMzJlNjhmZDBlNGM2YTVmYTZmNjFmM2U3N2VhYjdhZQ==
2
+ SHA1:
3
+ metadata.gz: 72fabe0d6ead6f78ac3e45619a500e6896e0c6c9
4
+ data.tar.gz: b53a9302344e97be08143b8ea814d5f2e5a3f092
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- YTU1M2YxOGEzNGFmZjE5YmE4NjZkNzk3Mjg4OTIxMGFjZWIwOGI1MTBmYmY0
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.0)
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.51.0)
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.3)
50
- ffi-yajl (1.0.2)
51
+ ffi (1.9.6)
52
+ ffi-yajl (1.3.1)
51
53
  ffi (~> 1.5)
52
- libyajl2 (~> 1.0)
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.1)
59
+ libyajl2 (1.2.0)
58
60
  method_source (0.8.2)
59
61
  mime-types (1.25.1)
60
- mini_portile (0.6.0)
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.1)
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.3.1)
75
- mini_portile (= 0.6.0)
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.5.2)
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.0)
115
+ trollop (2.1.1)
114
116
  wmi-lite (1.0.0)
115
117
 
116
118
  PLATFORMS
data/examples/stemcellrc CHANGED
@@ -1,6 +1,5 @@
1
1
  # these options need to be set
2
2
  export AWS_ACCESS_KEY=your_aws_access_key
3
- export AWS_SECRET_KEY=your_aws_secret_key
4
3
 
5
4
  # AWC keys can be managed from the AWS console by going
6
5
  # to EC2 and then clicking 'Key Pairs'. Every EC2 instance
@@ -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
@@ -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
- 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"
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(instances,opts={})
207
- return if instances.nil?
208
- instances.each do |i|
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(i)
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
- throw e unless opts[:ignore_not_found]
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 true
247
- sleep 5
248
- if Time.now - @start_time > @timeout
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
- end
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.each do |instance|
285
- instance.tags.set(tags)
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
@@ -1,3 +1,3 @@
1
1
  module Stemcell
2
- VERSION = "0.8.0"
2
+ VERSION = "0.8.1"
3
3
  end
@@ -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.0
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: 2014-08-21 00:00:00.000000000 Z
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.4.1
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