stemcell 0.12.0 → 0.13.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,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d0a73c0c28344f526396892ffde6de0054696d7f
4
- data.tar.gz: 3286ce5584f761e5e643f9fe02bd575f9199e651
2
+ SHA256:
3
+ metadata.gz: 9942211d9334ce09b4a56299f2bc7e3e903d57eb871a60a8a1228c549e6b1407
4
+ data.tar.gz: 3e70bbdc1434124922c4be3cfb29e6842d6cda8210a1555fe3c36e4887740a11
5
5
  SHA512:
6
- metadata.gz: 00ddd6ab1066baf755627d945f0fc033457f9256f891176a5af5b1b0d259fc93885d08a1d5b12cce4cbe42733be0b8af36ae6003fab521d7535cd00dd9da57e8
7
- data.tar.gz: 4e09e64bba35df73ec82622889863d3456b162a6d9405954f02bb0087709bbf9a0036d052d543823871ed712d0c6171222a2bddbd7ab9e94eb9bdf7f351a036b
6
+ metadata.gz: c4f0a32c8fe858b23bd3ddbbb30ca752a0b23c0a2782d9fd0cf56d9cda0e344bdaa3c2361b1df9f4d5f5413ca88e15c714c868ee2efaa1b304dd74d248601fdb
7
+ data.tar.gz: a9fb4751d0fe25b94f4c47785da168a9e0e909464d0354461adc8db5d01487d5ea696ebcbc79e74267b6ccd114a794a5e5d13ed90bd6b831a20c3bb5ae5b4906
data/.travis.yml CHANGED
@@ -5,5 +5,6 @@ rvm:
5
5
  - 2.1.2
6
6
  - 2.2.4
7
7
  - 2.3.1
8
+ - 2.6.8
8
9
  before_install:
9
- - gem install bundler
10
+ - gem install bundler -v 1.16.3
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 necrosis command and pass a space separated list of instance ids:
80
+ To terminate, use the AWS CLI and pass a space separated list of instance ids:
81
81
 
82
82
  ```bash
83
- $ necrosis i-12345678 i-12345679 i-12345670
83
+ $ aws ec2 terminate-instances --instance-ids i-12345678 i-12345679 i-12345670
84
84
  ```
85
85
 
86
86
  ## Automation ##
@@ -9,10 +9,14 @@
9
9
 
10
10
  "backing_store": {
11
11
  "ebs": {
12
- "image_id": "ami-23d9a94a"
12
+ "us-east-1": {
13
+ "image_id": "ami-23d9a94a"
14
+ }
13
15
  },
14
16
  "instance_store": {
15
- "image_id": "ami-d9d6a6b0"
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
 
@@ -1,4 +1,5 @@
1
- require 'aws-sdk-v1'
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 specefied as inputs
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
- :count => opts['count'],
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
- launch_options[:security_group_ids] = opts['security_group_ids']
124
+ network_interface[:groups] = opts['security_group_ids']
113
125
  end
114
126
 
115
127
  if opts['security_groups'] && !opts['security_groups'].empty?
116
- if @vpc_id
117
- # convert sg names to sg ids as VPC only accepts ids
118
- security_group_ids = get_vpc_security_group_ids(@vpc_id, opts['security_groups'])
119
- launch_options[:security_group_ids] ||= []
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
- launch_options[:availability_zone] = opts['availability_zone']
137
+ placement[:availability_zone] = opts['availability_zone']
129
138
  end
130
139
 
131
140
  if opts['subnet']
132
- launch_options[:subnet] = opts['subnet']
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
- launch_options[:dedicated_tenancy] = opts['dedicated_tenancy']
149
+ placement[:tenancy] = 'dedicated'
141
150
  end
142
151
 
143
152
  if opts['associate_public_ip_address']
144
- launch_options[:associate_public_ip_address] = opts['associate_public_ip_address']
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] = opts['iam_role']
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
- launch_options[:placement] = {
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
- wait(instances)
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(instances, opts={})
230
- return if !instances || instances.empty?
239
+ def kill(instance_ids, opts={})
240
+ return if !instance_ids || instance_ids.empty?
231
241
 
232
- errors = run_batch_operation(instances) do |instance|
233
- begin
234
- @log.warn "Terminating instance #{instance.id}"
235
- instance.terminate
236
- nil # nil == success
237
- rescue AWS::EC2::Errors::InvalidInstanceID::NotFound => e
238
- opts[:ignore_not_found] ? nil : e
239
- end
240
- end
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 "genereated template is #{generated_template}"
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(instances)
268
- @log.info "Waiting up to #{MAX_RUNNING_STATE_WAIT_TIME} seconds for #{instances.count} " \
269
- "instance(s): (#{instances.inspect})"
270
-
271
- times_out_at = Time.now + MAX_RUNNING_STATE_WAIT_TIME
272
- until instances.all?{ |i| i.status == :running }
273
- wait_time_expire_or_sleep(times_out_at)
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.instances.create(opts)
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
- vpc = AWS::EC2::VPC.new(vpc_id, :ec2_endpoint => "ec2.#{@region}.amazonaws.com")
318
- vpc.security_groups.each do |sg|
319
- next if sg.vpc_id != vpc_id
320
- group_map[sg.name] = sg.group_id
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
- return @ec2 if @ec2
434
-
435
- # calculate our ec2 url
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
- AWS.config(aws_configs)
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