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 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