stemcell 0.12.0 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +2 -1
- data/CHANGELOG.md +16 -0
- data/README.md +2 -2
- data/examples/stemcell.json +6 -2
- data/lib/stemcell/command_line.rb +1 -0
- data/lib/stemcell/launcher.rb +83 -200
- data/lib/stemcell/metadata_launcher.rb +1 -0
- data/lib/stemcell/metadata_source/configuration.rb +2 -2
- data/lib/stemcell/metadata_source.rb +6 -1
- data/lib/stemcell/option_parser.rb +22 -29
- data/lib/stemcell/templates/bootstrap.sh.erb +91 -20
- data/lib/stemcell/version.rb +1 -1
- data/spec/fixtures/chef_repo/stemcell-azs-missing.json +3 -1
- data/spec/fixtures/chef_repo/stemcell-backing-store-legacy.json +13 -0
- data/spec/fixtures/chef_repo/stemcell-cookbook-attribute.json +3 -1
- data/spec/fixtures/chef_repo/stemcell-defaults-missing.json +3 -1
- data/spec/fixtures/chef_repo/stemcell-options-parser.json +6 -2
- data/spec/fixtures/chef_repo/stemcell.json +3 -1
- data/spec/lib/stemcell/launcher_spec.rb +110 -123
- data/spec/lib/stemcell/metadata_source/configuration_spec.rb +16 -3
- data/spec/lib/stemcell/metadata_source_spec.rb +2 -1
- data/stemcell.gemspec +5 -2
- metadata +36 -9
- data/bin/necrosis +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9942211d9334ce09b4a56299f2bc7e3e903d57eb871a60a8a1228c549e6b1407
|
4
|
+
data.tar.gz: 3e70bbdc1434124922c4be3cfb29e6842d6cda8210a1555fe3c36e4887740a11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4f0a32c8fe858b23bd3ddbbb30ca752a0b23c0a2782d9fd0cf56d9cda0e344bdaa3c2361b1df9f4d5f5413ca88e15c714c868ee2efaa1b304dd74d248601fdb
|
7
|
+
data.tar.gz: a9fb4751d0fe25b94f4c47785da168a9e0e909464d0354461adc8db5d01487d5ea696ebcbc79e74267b6ccd114a794a5e5d13ed90bd6b831a20c3bb5ae5b4906
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# 0.13.1
|
2
|
+
- Support for specifying --cpu-options
|
3
|
+
|
4
|
+
# 0.13.0
|
5
|
+
- Migrate to AWS SDK to v3
|
6
|
+
- Drop support for ClassicLink
|
7
|
+
- Removed `necrosis` script
|
8
|
+
|
9
|
+
# 0.12.2
|
10
|
+
- Support for using a custom EC2 endpoint
|
11
|
+
|
12
|
+
# 0.12.1
|
13
|
+
- Add support for Amazon Linux to the default bootstrap script
|
14
|
+
- Allow setting backing_store options per region
|
15
|
+
- Display private ip for launched instances
|
16
|
+
|
1
17
|
# 0.12.0
|
2
18
|
- Require Nokogiri ~> 1.8.2 due to vulnerability CVE-2017-15412
|
3
19
|
- Require ruby version >= 2.1 for Nokogiri compatibility
|
data/README.md
CHANGED
@@ -77,10 +77,10 @@ $ stemcell $your_chef_role --tail
|
|
77
77
|
|
78
78
|
### Terminating:
|
79
79
|
|
80
|
-
To terminate, use the
|
80
|
+
To terminate, use the AWS CLI and pass a space separated list of instance ids:
|
81
81
|
|
82
82
|
```bash
|
83
|
-
$
|
83
|
+
$ aws ec2 terminate-instances --instance-ids i-12345678 i-12345679 i-12345670
|
84
84
|
```
|
85
85
|
|
86
86
|
## Automation ##
|
data/examples/stemcell.json
CHANGED
@@ -9,10 +9,14 @@
|
|
9
9
|
|
10
10
|
"backing_store": {
|
11
11
|
"ebs": {
|
12
|
-
"
|
12
|
+
"us-east-1": {
|
13
|
+
"image_id": "ami-23d9a94a"
|
14
|
+
}
|
13
15
|
},
|
14
16
|
"instance_store": {
|
15
|
-
"
|
17
|
+
"us-east-1": {
|
18
|
+
"image_id": "ami-d9d6a6b0"
|
19
|
+
}
|
16
20
|
}
|
17
21
|
},
|
18
22
|
|
@@ -132,6 +132,7 @@ module Stemcell
|
|
132
132
|
# comma-separated list when presented as defaults.
|
133
133
|
pd['security_groups'] &&= pd['security_groups'].join(',')
|
134
134
|
pd['tags'] &&= pd['tags'].to_a.map { |p| p.join('=') }.join(',')
|
135
|
+
pd['cpu_options'] &&= pd['cpu_options'].to_a.map { |p| p.join('=') }.join(',')
|
135
136
|
pd['chef_cookbook_attributes'] &&= pd['chef_cookbook_attributes'].join(',')
|
136
137
|
end
|
137
138
|
|
data/lib/stemcell/launcher.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'aws-sdk-
|
1
|
+
require 'aws-sdk-ec2'
|
2
|
+
require 'base64'
|
2
3
|
require 'logger'
|
3
4
|
require 'erb'
|
4
5
|
require 'set'
|
@@ -30,6 +31,7 @@ module Stemcell
|
|
30
31
|
'chef_environment',
|
31
32
|
'chef_data_bag_secret',
|
32
33
|
'chef_data_bag_secret_path',
|
34
|
+
'cpu_options',
|
33
35
|
'git_branch',
|
34
36
|
'git_key',
|
35
37
|
'git_origin',
|
@@ -48,7 +50,6 @@ module Stemcell
|
|
48
50
|
'security_groups',
|
49
51
|
'security_group_ids',
|
50
52
|
'tags',
|
51
|
-
'classic_link',
|
52
53
|
'iam_role',
|
53
54
|
'ebs_optimized',
|
54
55
|
'termination_protection',
|
@@ -75,6 +76,7 @@ module Stemcell
|
|
75
76
|
|
76
77
|
@region = opts['region']
|
77
78
|
@vpc_id = opts['vpc_id']
|
79
|
+
@ec2_endpoint = opts['ec2_endpoint']
|
78
80
|
@aws_access_key = opts['aws_access_key']
|
79
81
|
@aws_secret_key = opts['aws_secret_key']
|
80
82
|
@aws_session_token = opts['aws_session_token']
|
@@ -89,7 +91,7 @@ module Stemcell
|
|
89
91
|
opts['git_key'] = try_file(opts['git_key'])
|
90
92
|
opts['chef_data_bag_secret'] = try_file(opts['chef_data_bag_secret'])
|
91
93
|
|
92
|
-
# generate tags and merge in any that were
|
94
|
+
# generate tags and merge in any that were specified as inputs
|
93
95
|
tags = {
|
94
96
|
'Name' => "#{opts['chef_role']}-#{opts['chef_environment']}",
|
95
97
|
'Group' => "#{opts['chef_role']}-#{opts['chef_environment']}",
|
@@ -105,31 +107,38 @@ module Stemcell
|
|
105
107
|
:image_id => opts['image_id'],
|
106
108
|
:instance_type => opts['instance_type'],
|
107
109
|
:key_name => opts['key_name'],
|
108
|
-
:
|
110
|
+
:min_count => opts['count'],
|
111
|
+
:max_count => opts['count'],
|
109
112
|
}
|
110
113
|
|
114
|
+
|
115
|
+
# Associate Public IP can only bet set on network_interfaces, and if present
|
116
|
+
# security groups and subnet should be set on the interface. VPC-only.
|
117
|
+
# Primary network interface
|
118
|
+
network_interface = {
|
119
|
+
device_index: 0,
|
120
|
+
}
|
121
|
+
launch_options[:network_interfaces] = [network_interface]
|
122
|
+
|
111
123
|
if opts['security_group_ids'] && !opts['security_group_ids'].empty?
|
112
|
-
|
124
|
+
network_interface[:groups] = opts['security_group_ids']
|
113
125
|
end
|
114
126
|
|
115
127
|
if opts['security_groups'] && !opts['security_groups'].empty?
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
launch_options[:security_group_ids].concat(security_group_ids)
|
121
|
-
else
|
122
|
-
launch_options[:security_groups] = opts['security_groups']
|
123
|
-
end
|
128
|
+
# convert sg names to sg ids as VPC only accepts ids
|
129
|
+
security_group_ids = get_vpc_security_group_ids(@vpc_id, opts['security_groups'])
|
130
|
+
network_interface[:groups] ||= []
|
131
|
+
network_interface[:groups].concat(security_group_ids)
|
124
132
|
end
|
125
133
|
|
134
|
+
launch_options[:placement] = placement = {}
|
126
135
|
# specify availability zone (optional)
|
127
136
|
if opts['availability_zone']
|
128
|
-
|
137
|
+
placement[:availability_zone] = opts['availability_zone']
|
129
138
|
end
|
130
139
|
|
131
140
|
if opts['subnet']
|
132
|
-
|
141
|
+
network_interface[:subnet_id] = opts['subnet']
|
133
142
|
end
|
134
143
|
|
135
144
|
if opts['private_ip_address']
|
@@ -137,23 +146,23 @@ module Stemcell
|
|
137
146
|
end
|
138
147
|
|
139
148
|
if opts['dedicated_tenancy']
|
140
|
-
|
149
|
+
placement[:tenancy] = 'dedicated'
|
141
150
|
end
|
142
151
|
|
143
152
|
if opts['associate_public_ip_address']
|
144
|
-
|
153
|
+
network_interface[:associate_public_ip_address] = opts['associate_public_ip_address']
|
145
154
|
end
|
146
155
|
|
147
156
|
# specify IAM role (optional)
|
148
157
|
if opts['iam_role']
|
149
|
-
launch_options[:iam_instance_profile] =
|
158
|
+
launch_options[:iam_instance_profile] = {
|
159
|
+
name: opts['iam_role']
|
160
|
+
}
|
150
161
|
end
|
151
162
|
|
152
163
|
# specify placement group (optional)
|
153
164
|
if opts['placement_group']
|
154
|
-
|
155
|
-
:group_name => opts['placement_group'],
|
156
|
-
}
|
165
|
+
placement[:group_name] = opts['placement_group']
|
157
166
|
end
|
158
167
|
|
159
168
|
# specify an EBS-optimized instance (optional)
|
@@ -170,6 +179,11 @@ module Stemcell
|
|
170
179
|
launch_options[:block_device_mappings] = opts['block_device_mappings']
|
171
180
|
end
|
172
181
|
|
182
|
+
# specify cpu options (optional)
|
183
|
+
if opts['cpu_options']
|
184
|
+
launch_options[:cpu_options] = opts['cpu_options']
|
185
|
+
end
|
186
|
+
|
173
187
|
# specify ephemeral block device mappings (optional)
|
174
188
|
if opts['ephemeral_devices']
|
175
189
|
launch_options[:block_device_mappings] ||= []
|
@@ -181,35 +195,31 @@ module Stemcell
|
|
181
195
|
end
|
182
196
|
end
|
183
197
|
|
198
|
+
if opts['termination_protection']
|
199
|
+
launch_options[:disable_api_termination] = true
|
200
|
+
end
|
201
|
+
|
184
202
|
# generate user data script to bootstrap instance, include in launch
|
185
203
|
# options UNLESS we have manually set the user-data (ie. for ec2admin)
|
186
|
-
launch_options[:user_data] = opts.fetch('user_data', render_template(opts))
|
204
|
+
launch_options[:user_data] = Base64.encode64(opts.fetch('user_data', render_template(opts)))
|
205
|
+
|
206
|
+
# add tags to launch options so we don't need to make a separate CreateTags call
|
207
|
+
launch_options[:tag_specifications] = [{
|
208
|
+
resource_type: 'instance',
|
209
|
+
tags: tags.map { |k, v| { key: k, value: v } }
|
210
|
+
}]
|
187
211
|
|
188
212
|
# launch instances
|
189
213
|
instances = do_launch(launch_options)
|
190
214
|
|
191
215
|
# everything from here on out must succeed, or we kill the instances we just launched
|
192
216
|
begin
|
193
|
-
# set tags on all instances launched
|
194
|
-
set_tags(instances, tags)
|
195
|
-
@log.info "sent ec2 api tag requests successfully"
|
196
|
-
|
197
|
-
# link to classiclink
|
198
|
-
unless @vpc_id
|
199
|
-
set_classic_link(instances, opts['classic_link'])
|
200
|
-
@log.info "successfully applied classic link settings (if any)"
|
201
|
-
end
|
202
|
-
|
203
|
-
# turn on termination protection
|
204
|
-
# we do this now to make sure all other settings worked
|
205
|
-
if opts['termination_protection']
|
206
|
-
enable_termination_protection(instances)
|
207
|
-
@log.info "successfully enabled termination protection"
|
208
|
-
end
|
209
|
-
|
210
217
|
# wait for aws to report instance stats
|
211
218
|
if opts.fetch('wait', true)
|
212
|
-
|
219
|
+
instance_ids = instances.map(&:instance_id)
|
220
|
+
@log.info "Waiting up to #{MAX_RUNNING_STATE_WAIT_TIME} seconds for #{instances.count} " \
|
221
|
+
"instance(s): (#{instance_ids})"
|
222
|
+
instances = wait(instance_ids)
|
213
223
|
print_run_info(instances)
|
214
224
|
@log.info "launched instances successfully"
|
215
225
|
end
|
@@ -226,19 +236,18 @@ module Stemcell
|
|
226
236
|
return instances
|
227
237
|
end
|
228
238
|
|
229
|
-
def kill(
|
230
|
-
return if !
|
239
|
+
def kill(instance_ids, opts={})
|
240
|
+
return if !instance_ids || instance_ids.empty?
|
231
241
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
check_errors(:kill, instances.map(&:id), errors)
|
242
|
+
@log.warn "Terminating instances #{instance_ids}"
|
243
|
+
ec2.terminate_instances(instance_ids: instance_ids)
|
244
|
+
nil # nil == success
|
245
|
+
rescue Aws::EC2::Errors::InvalidInstanceIDNotFound => e
|
246
|
+
raise unless opts[:ignore_not_found]
|
247
|
+
|
248
|
+
invalid_ids = e.message.scan(/i-[a-z0-9]+/)
|
249
|
+
instance_ids -= invalid_ids
|
250
|
+
retry unless instance_ids.empty? || invalid_ids.empty? # don't retry if we couldn't find any instance ids
|
242
251
|
end
|
243
252
|
|
244
253
|
# this is made public for ec2admin usage
|
@@ -248,7 +257,7 @@ module Stemcell
|
|
248
257
|
erb_template = ERB.new(template_file)
|
249
258
|
last_bootstrap_line = LAST_BOOTSTRAP_LINE
|
250
259
|
generated_template = erb_template.result(binding)
|
251
|
-
@log.debug "
|
260
|
+
@log.debug "generated template is #{generated_template}"
|
252
261
|
return generated_template
|
253
262
|
end
|
254
263
|
|
@@ -258,22 +267,23 @@ module Stemcell
|
|
258
267
|
puts "\nhere is the info for what's launched:"
|
259
268
|
instances.each do |instance|
|
260
269
|
puts "\tinstance_id: #{instance.instance_id}"
|
261
|
-
puts "\tpublic ip: #{instance.public_ip_address}"
|
270
|
+
puts "\tpublic ip: #{instance.public_ip_address || 'none'}"
|
271
|
+
puts "\tprivate ip: #{instance.private_ip_address || 'none'}"
|
262
272
|
puts
|
263
273
|
end
|
264
274
|
puts "install logs will be in /var/log/init and /var/log/init.err"
|
265
275
|
end
|
266
276
|
|
267
|
-
def wait(
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
277
|
+
def wait(instance_ids)
|
278
|
+
started_at = Time.now
|
279
|
+
result = ec2.wait_until(:instance_running, instance_ids: instance_ids) do |w|
|
280
|
+
w.max_attempts = nil
|
281
|
+
w.delay = RUNNING_STATE_WAIT_SLEEP_TIME
|
282
|
+
w.before_wait do |attempts, response|
|
283
|
+
throw :failure if Time.now - started_at > MAX_RUNNING_STATE_WAIT_TIME
|
284
|
+
end
|
274
285
|
end
|
275
|
-
|
276
|
-
@log.info "all instances in running state"
|
286
|
+
result.map { |page| page.reservations.map(&:instances) }.flatten
|
277
287
|
end
|
278
288
|
|
279
289
|
def verify_required_options(params, required_options)
|
@@ -289,35 +299,22 @@ module Stemcell
|
|
289
299
|
def do_launch(opts={})
|
290
300
|
@log.debug "about to launch instance(s) with options #{opts}"
|
291
301
|
@log.info "launching instances"
|
292
|
-
instances = ec2.
|
293
|
-
instances = [instances] unless Array === instances
|
302
|
+
instances = ec2.run_instances(opts).instances
|
294
303
|
instances.each do |instance|
|
295
304
|
@log.info "launched instance #{instance.instance_id}"
|
296
305
|
end
|
297
306
|
return instances
|
298
307
|
end
|
299
308
|
|
300
|
-
def set_tags(instances=[], tags)
|
301
|
-
@log.info "setting tags on instance(s)"
|
302
|
-
errors = run_batch_operation(instances) do |instance|
|
303
|
-
begin
|
304
|
-
instance.tags.set(tags)
|
305
|
-
nil # nil == success
|
306
|
-
rescue AWS::EC2::Errors::InvalidInstanceID::NotFound => e
|
307
|
-
e
|
308
|
-
end
|
309
|
-
end
|
310
|
-
check_errors(:set_tags, instances.map(&:id), errors)
|
311
|
-
end
|
312
|
-
|
313
309
|
# Resolve security group names to their ids in the given VPC
|
314
310
|
def get_vpc_security_group_ids(vpc_id, group_names)
|
315
311
|
group_map = {}
|
316
312
|
@log.info "resolving security groups #{group_names} in #{vpc_id}"
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
313
|
+
ec2.describe_security_groups(filters: [{ name: 'vpc-id', values: [vpc_id] }]).
|
314
|
+
each do |response|
|
315
|
+
response.security_groups.each do |sg|
|
316
|
+
group_map[sg.group_name] = sg.group_id
|
317
|
+
end
|
321
318
|
end
|
322
319
|
group_ids = []
|
323
320
|
group_names.each do |sg_name|
|
@@ -327,129 +324,15 @@ module Stemcell
|
|
327
324
|
group_ids
|
328
325
|
end
|
329
326
|
|
330
|
-
def set_classic_link(left_to_process, classic_link)
|
331
|
-
return unless classic_link
|
332
|
-
return unless classic_link['vpc_id']
|
333
|
-
|
334
|
-
security_group_ids = classic_link['security_group_ids'] || []
|
335
|
-
security_group_names = classic_link['security_groups'] || []
|
336
|
-
return if security_group_ids.empty? && security_group_names.empty?
|
337
|
-
|
338
|
-
if !security_group_names.empty?
|
339
|
-
extra_group_ids = get_vpc_security_group_ids(classic_link['vpc_id'], security_group_names)
|
340
|
-
security_group_ids = security_group_ids + extra_group_ids
|
341
|
-
end
|
342
|
-
|
343
|
-
@log.info "applying classic link settings on #{left_to_process.count} instance(s)"
|
344
|
-
|
345
|
-
errors = []
|
346
|
-
processed = []
|
347
|
-
times_out_at = Time.now + MAX_RUNNING_STATE_WAIT_TIME
|
348
|
-
until left_to_process.empty?
|
349
|
-
wait_time_expire_or_sleep(times_out_at)
|
350
|
-
|
351
|
-
# we can only apply classic link when instances are in the running state
|
352
|
-
# lets apply classiclink as instances become available so we don't wait longer than necessary
|
353
|
-
recently_running = left_to_process.select{ |inst| inst.status == :running }
|
354
|
-
left_to_process = left_to_process.reject{ |inst| recently_running.include?(inst) }
|
355
|
-
|
356
|
-
processed += recently_running
|
357
|
-
errors += run_batch_operation(recently_running) do |instance|
|
358
|
-
begin
|
359
|
-
result = ec2.client.attach_classic_link_vpc({
|
360
|
-
:instance_id => instance.id,
|
361
|
-
:vpc_id => classic_link['vpc_id'],
|
362
|
-
:groups => security_group_ids,
|
363
|
-
})
|
364
|
-
result.error
|
365
|
-
rescue StandardError => e
|
366
|
-
e
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
check_errors(:set_classic_link, processed.map(&:id), errors)
|
372
|
-
end
|
373
|
-
|
374
|
-
def enable_termination_protection(instances)
|
375
|
-
@log.info "enabling termination protection on instance(s)"
|
376
|
-
errors = run_batch_operation(instances) do |instance|
|
377
|
-
begin
|
378
|
-
resp = ec2.client.modify_instance_attribute({
|
379
|
-
:instance_id => instance.id,
|
380
|
-
:disable_api_termination => {
|
381
|
-
:value => true
|
382
|
-
}
|
383
|
-
})
|
384
|
-
resp.error # returns nil (success) unless there was an error
|
385
|
-
rescue StandardError => e
|
386
|
-
e
|
387
|
-
end
|
388
|
-
end
|
389
|
-
check_errors(:enable_termination_protection, instances.map(&:id), errors)
|
390
|
-
end
|
391
|
-
|
392
327
|
# attempt to accept keys as file paths
|
393
328
|
def try_file(opt="")
|
394
329
|
File.read(File.expand_path(opt)) rescue opt
|
395
330
|
end
|
396
331
|
|
397
|
-
INITIAL_RETRY_SEC = 1
|
398
|
-
|
399
|
-
# Return a Hash of instance => error. Empty hash indicates "no error"
|
400
|
-
# for code block:
|
401
|
-
# - if block returns nil, success
|
402
|
-
# - if block returns non-nil value (e.g., exception), retry 3 times w/ backoff
|
403
|
-
# - if block raises exception, fail
|
404
|
-
def run_batch_operation(instances)
|
405
|
-
instances.map do |instance|
|
406
|
-
begin
|
407
|
-
attempt = 0
|
408
|
-
result = nil
|
409
|
-
while attempt < @max_attempts
|
410
|
-
# sleep idempotently except for the first attempt
|
411
|
-
sleep(INITIAL_RETRY_SEC * 2 ** attempt) if attempt != 0
|
412
|
-
result = yield(instance)
|
413
|
-
break if result.nil? # nil indicates success
|
414
|
-
attempt += 1
|
415
|
-
end
|
416
|
-
result # result for this instance is nil or returned exception
|
417
|
-
rescue => e
|
418
|
-
e # result for this instance is caught exception
|
419
|
-
end
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
def check_errors(operation, instance_ids, errors)
|
424
|
-
return if errors.all?(&:nil?)
|
425
|
-
raise IncompleteOperation.new(
|
426
|
-
operation,
|
427
|
-
instance_ids,
|
428
|
-
instance_ids.zip(errors).reject { |i, e| e.nil? }
|
429
|
-
)
|
430
|
-
end
|
431
|
-
|
432
332
|
def ec2
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
ec2_url = "ec2.#{@region}.amazonaws.com"
|
437
|
-
|
438
|
-
if @vpc_id
|
439
|
-
@ec2 = AWS::EC2::VPC.new(@vpc_id, :ec2_endpoint => ec2_url)
|
440
|
-
else
|
441
|
-
@ec2 = AWS::EC2.new(:ec2_endpoint => ec2_url)
|
442
|
-
end
|
443
|
-
|
444
|
-
@ec2
|
445
|
-
end
|
446
|
-
|
447
|
-
def wait_time_expire_or_sleep(times_out_at)
|
448
|
-
now = Time.now
|
449
|
-
if now >= times_out_at
|
450
|
-
raise TimeoutError, "exceded timeout of #{MAX_RUNNING_STATE_WAIT_TIME} seconds"
|
451
|
-
else
|
452
|
-
sleep [RUNNING_STATE_WAIT_SLEEP_TIME, times_out_at - now].min
|
333
|
+
@ec2 ||= begin
|
334
|
+
opts = @ec2_endpoint ? { endpoint: @ec2_endpoint } : {}
|
335
|
+
Aws::EC2::Client.new(opts)
|
453
336
|
end
|
454
337
|
end
|
455
338
|
|
@@ -463,7 +346,7 @@ module Stemcell
|
|
463
346
|
aws_configs.merge!({
|
464
347
|
:session_token => @aws_session_token,
|
465
348
|
}) if @aws_session_token
|
466
|
-
|
349
|
+
Aws.config.update(aws_configs)
|
467
350
|
end
|
468
351
|
end
|
469
352
|
end
|
@@ -101,6 +101,7 @@ module Stemcell
|
|
101
101
|
'region' => options['region'],
|
102
102
|
'vpc_id' => options['vpc_id'],
|
103
103
|
'max_attempts' => options['batch_operation_retries'],
|
104
|
+
'ec2_endpoint' => options['ec2_endpoint'],
|
104
105
|
})
|
105
106
|
# Slice off just the options used for launching.
|
106
107
|
launch_options = {}
|
@@ -21,10 +21,10 @@ module Stemcell
|
|
21
21
|
validate_configutation
|
22
22
|
end
|
23
23
|
|
24
|
-
def options_for_backing_store(backing_store)
|
24
|
+
def options_for_backing_store(backing_store, region)
|
25
25
|
options = backing_store_options[backing_store]
|
26
26
|
raise UnknownBackingStoreError.new(backing_store) if options.nil?
|
27
|
-
options
|
27
|
+
options.fetch(region, options)
|
28
28
|
end
|
29
29
|
|
30
30
|
def random_az_for_region(region)
|
@@ -84,9 +84,14 @@ module Stemcell
|
|
84
84
|
backing_store ||= config.default_options['backing_store']
|
85
85
|
backing_store ||= DEFAULT_BACKING_STORE
|
86
86
|
|
87
|
+
backing_store_region = override_options['region']
|
88
|
+
backing_store_region ||= role_options.to_hash['region'] if role_options
|
89
|
+
backing_store_region ||= config.default_options['region']
|
90
|
+
backing_store_region ||= DEFAULT_OPTIONS['region']
|
91
|
+
|
87
92
|
# Step 3: Retrieve the backing store options from the defaults.
|
88
93
|
|
89
|
-
backing_store_options = config.options_for_backing_store(backing_store)
|
94
|
+
backing_store_options = config.options_for_backing_store(backing_store, backing_store_region)
|
90
95
|
backing_store_options['backing_store'] = backing_store
|
91
96
|
|
92
97
|
# Step 4: Merge the options together in priority order.
|
@@ -42,6 +42,12 @@ module Stemcell
|
|
42
42
|
:env => 'AWS_SESSION_TOKEN',
|
43
43
|
:hide => true
|
44
44
|
},
|
45
|
+
{
|
46
|
+
:name => 'ec2_endpoint',
|
47
|
+
:desc => 'EC2 endpoint',
|
48
|
+
:type => String,
|
49
|
+
:env => 'EC2_ENDPOINT',
|
50
|
+
},
|
45
51
|
{
|
46
52
|
:name => 'region',
|
47
53
|
:desc => "ec2 region to launch in",
|
@@ -90,24 +96,6 @@ module Stemcell
|
|
90
96
|
:type => String,
|
91
97
|
:env => 'VPC_ID'
|
92
98
|
},
|
93
|
-
{
|
94
|
-
:name => 'classic_link_vpc_id',
|
95
|
-
:desc => 'VPC ID to which this instance will be classic-linked',
|
96
|
-
:type => String,
|
97
|
-
:env => 'CLASSIC_LINK_VPC_ID',
|
98
|
-
},
|
99
|
-
{
|
100
|
-
:name => 'classic_link_security_group_ids',
|
101
|
-
:desc => 'comma-separated list of security group IDs to link into ClassicLink; not used unless classic_link_vpc_id is set',
|
102
|
-
:type => String,
|
103
|
-
:env => 'CLASSIC_LINK_SECURITY_GROUP_IDS',
|
104
|
-
},
|
105
|
-
{
|
106
|
-
:name => 'classic_link_security_groups',
|
107
|
-
:desc => 'comma-separated list of security groups to link into ClassicLink; not used unless classic_link_vpc_id is set',
|
108
|
-
:type => String,
|
109
|
-
:env => 'CLASSIC_LINK_SECURITY_GROUPS',
|
110
|
-
},
|
111
99
|
{
|
112
100
|
:name => 'subnet',
|
113
101
|
:desc => "VPC subnet for which to launch this instance",
|
@@ -199,6 +187,12 @@ module Stemcell
|
|
199
187
|
:type => String,
|
200
188
|
:env => 'CHEF_DATA_BAG_SECRET_PATH'
|
201
189
|
},
|
190
|
+
{
|
191
|
+
:name => 'cpu_options',
|
192
|
+
:desc => "comma-separated list of cpu option key=value pairs",
|
193
|
+
:type => String,
|
194
|
+
:env => 'CPU_OPTIONS'
|
195
|
+
},
|
202
196
|
{
|
203
197
|
:name => 'chef_role',
|
204
198
|
:desc => "chef role of instance to be launched",
|
@@ -357,6 +351,16 @@ module Stemcell
|
|
357
351
|
options['tags'] = tags
|
358
352
|
end
|
359
353
|
|
354
|
+
# convert cpu_options from comma separated string to ruby hash
|
355
|
+
if options['cpu_options']
|
356
|
+
cpu_options = {}
|
357
|
+
options['cpu_options'].split(',').each do |cpu_opt|
|
358
|
+
key, value = cpu_opt.split('=')
|
359
|
+
cpu_options[key] = value
|
360
|
+
end
|
361
|
+
options['cpu_options'] = cpu_options
|
362
|
+
end
|
363
|
+
|
360
364
|
# parse block_device_mappings to convert it from the standard CLI format
|
361
365
|
# to the EC2 Ruby API format.
|
362
366
|
# All of this is a bit hard to find so here are some docs links to
|
@@ -424,17 +428,6 @@ module Stemcell
|
|
424
428
|
# convert chef_cookbook_attributes from comma separated string to ruby array
|
425
429
|
options['chef_cookbook_attributes'] &&= options['chef_cookbook_attributes'].split(',')
|
426
430
|
|
427
|
-
# format the classic link options
|
428
|
-
if options['classic_link_vpc_id']
|
429
|
-
options['classic_link']['vpc_id'] = options['classic_link_vpc_id']
|
430
|
-
end
|
431
|
-
if options['classic_link_security_group_ids']
|
432
|
-
options['classic_link']['security_group_ids'] = options['classic_link_security_group_ids'].split(',')
|
433
|
-
end
|
434
|
-
if options['classic_link_security_groups']
|
435
|
-
options['classic_link']['security_groups'] = options['classic_link_security_groups'].split(',')
|
436
|
-
end
|
437
|
-
|
438
431
|
options
|
439
432
|
end
|
440
433
|
|