bosh_aws_cpi 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51b0fde31ccef32f21fe160d8e6ae7be77368530
4
- data.tar.gz: e3b8224c4f39a0d63b64baf7efdb6b9dcc3e8d8c
3
+ metadata.gz: ab03ce5aecf1e03ddb369df4a0906dda5b5d16fd
4
+ data.tar.gz: 673d0abaf1b22ea61797c997b70fdae0608ff24c
5
5
  SHA512:
6
- metadata.gz: 73e29668c5edcb7e6c2101d040cafb22947262397e2976175b40bce7f38fe9d174deccc52a3550aa4c9a5cd2fa325881e9a84a99271cdc88217d27277566fc45
7
- data.tar.gz: fa984ef1fa766aa9945260e513781846e71957fabcebcde03e0249e8a9582641b2bb6912f296eb81706bff5e371d15659073f8eb3b19d662111fba1bf7f19a58
6
+ metadata.gz: be78232ecfe2fb479b23ab635571050c9f33609e060dd0f63d45a78a2615b648e93161294afb652442ac6d40de049d6c3d33b23eeb3eb43ff973025bd05d452d
7
+ data.tar.gz: 5c86071d7c35194024548bab7d07c8e373b188ffe69dc7d7008584bac283d14625adbf865383faa0b8b2a5629c00e6f98d3d41a573dcf0a7d62e4f92c0d95de8
@@ -36,6 +36,9 @@ require "cloud/aws/spot_manager"
36
36
  require "cloud/aws/tag_manager"
37
37
  require "cloud/aws/availability_zone_selector"
38
38
  require "cloud/aws/resource_wait"
39
+ require "cloud/aws/volume_properties"
40
+ require "cloud/aws/volumes_create_presenter"
41
+ require "cloud/aws/instances_create_presenter"
39
42
 
40
43
  module Bosh
41
44
  module Clouds
@@ -10,10 +10,8 @@ module Bosh::AwsCloud
10
10
  def common_availability_zone(volume_az_names, resource_pool_az_name, vpc_subnet_az_name)
11
11
  zone_names = (volume_az_names + [resource_pool_az_name, vpc_subnet_az_name]).compact.uniq
12
12
  if zone_names.size > 1
13
- raise Bosh::Clouds::CloudError,
14
- "can't use multiple availability zones: Volume in #{volume_az_names.join(', ')}, " +
15
- "Resource Pool in #{resource_pool_az_name}, " +
16
- "Subnet in #{vpc_subnet_az_name}"
13
+ volume_az_error_string = ", and volume in #{volume_az_names.join(', ')}" unless volume_az_names.empty?
14
+ raise Bosh::Clouds::CloudError, "can't use multiple availability zones: subnet in #{vpc_subnet_az_name}, VM in #{resource_pool_az_name}#{volume_az_error_string}"
17
15
  end
18
16
 
19
17
  zone_names.first || @default
@@ -1,8 +1,6 @@
1
- # Copyright (c) 2009-2012 VMware, Inc.
2
1
  require 'cloud/aws/stemcell_finder'
3
2
 
4
3
  module Bosh::AwsCloud
5
-
6
4
  class Cloud < Bosh::Cloud
7
5
  include Helpers
8
6
 
@@ -33,8 +31,6 @@ module Bosh::AwsCloud
33
31
  @aws_params = {
34
32
  credentials_source: aws_properties['credentials_source'] || 'static',
35
33
  region: aws_properties['region'],
36
- ec2_endpoint: aws_properties['ec2_endpoint'] || default_ec2_endpoint,
37
- elb_endpoint: aws_properties['elb_endpoint'] || default_elb_endpoint,
38
34
  max_retries: aws_properties['max_retries'] || DEFAULT_MAX_RETRIES,
39
35
  logger: aws_logger
40
36
  }
@@ -156,10 +152,6 @@ module Bosh::AwsCloud
156
152
  end
157
153
  end
158
154
 
159
- def default_elb_endpoint
160
- ['elasticloadbalancing', aws_region, 'amazonaws.com'].compact.join('.')
161
- end
162
-
163
155
  ##
164
156
  # Delete EC2 instance ("terminate" in AWS language) and wait until
165
157
  # it reports as terminated
@@ -196,17 +188,20 @@ module Bosh::AwsCloud
196
188
  # of the VM that this disk will be attached to
197
189
  # @return [String] created EBS volume id
198
190
  def create_disk(size, cloud_properties, instance_id = nil)
191
+ raise ArgumentError, 'disk size needs to be an integer' unless size.kind_of?(Integer)
199
192
  with_thread_name("create_disk(#{size}, #{instance_id})") do
200
- type = validate_disk_type(cloud_properties.fetch('type', 'standard'))
201
- validate_disk_size(type, size)
193
+ volume_properties = VolumeProperties.new(
194
+ size: size,
195
+ type: cloud_properties['type'],
196
+ iops: cloud_properties['iops'],
197
+ az: @az_selector.select_availability_zone(instance_id),
198
+ encrypted: cloud_properties['encrypted']
199
+ )
200
+ volume_properties.validate!
202
201
 
203
202
  # if the disk is created for an instance, use the same availability zone as they must match
204
- volume = @ec2.volumes.create(
205
- size: (size / 1024.0).ceil,
206
- availability_zone: @az_selector.select_availability_zone(instance_id),
207
- volume_type: type,
208
- encrypted: cloud_properties.fetch('encrypted', false)
209
- )
203
+ with_volume_options = VolumesCreatePresenter.new(volume_properties).present
204
+ volume = @ec2.volumes.create(with_volume_options)
210
205
 
211
206
  logger.info("Creating volume '#{volume.id}'")
212
207
  ResourceWait.for_volume(volume: volume, state: :available)
@@ -215,28 +210,10 @@ module Bosh::AwsCloud
215
210
  end
216
211
  end
217
212
 
218
- def validate_disk_size(type, size)
219
- raise ArgumentError, 'disk size needs to be an integer' unless size.kind_of?(Integer)
220
-
221
- cloud_error('AWS CPI minimum disk size is 1 GiB') if size < 1024
222
- if type == 'standard'
223
- cloud_error('AWS CPI maximum disk size is 1 TiB') if size > 1024 * 1000
224
- else
225
- cloud_error('AWS CPI maximum disk size is 16 TiB') if size > 1024 * 16000
226
- end
227
- end
228
-
229
- def validate_disk_type(type)
230
- unless %w[gp2 standard io1].include?(type)
231
- cloud_error('AWS CPI supports only gp2, io1, or standard disk type')
232
- end
233
- type
234
- end
235
-
236
213
  ##
237
- # Check whether an OpenStack volume exists or not
214
+ # Check whether an EBS volume exists or not
238
215
  #
239
- # @param [String] disk_id OpenStack volume UUID
216
+ # @param [String] disk_id EBS volume UUID
240
217
  # @return [bool] whether the specific disk is there or not
241
218
  def has_disk?(disk_id)
242
219
  with_thread_name("has_disk?(#{disk_id})") do
@@ -313,6 +290,9 @@ module Bosh::AwsCloud
313
290
  end
314
291
  logger.info("Attached `#{disk_id}' to `#{instance_id}'")
315
292
  end
293
+
294
+ # log registry settings for debugging
295
+ logger.debug("updated registry settings: #{registry.read_settings(instance_id)}")
316
296
  end
317
297
 
318
298
  # Detach an EBS volume from an EC2 instance
@@ -523,7 +503,13 @@ module Bosh::AwsCloud
523
503
  instance = @ec2.instances[vm]
524
504
 
525
505
  metadata.each_pair do |key, value|
526
- TagManager.tag(instance, key, value)
506
+ TagManager.tag(instance, key, value) unless key == 'name'
507
+ end
508
+
509
+ name = metadata['name']
510
+ if name
511
+ TagManager.tag(instance, "Name", name)
512
+ return
527
513
  end
528
514
 
529
515
  job = metadata['job']
@@ -559,11 +545,6 @@ module Bosh::AwsCloud
559
545
  attr_reader :az_selector
560
546
  attr_reader :region
561
547
 
562
- def default_ec2_endpoint
563
- region_suffix = (aws_region && aws_region.start_with?('cn')) ? 'cn' : nil
564
- ['ec2', aws_region, 'amazonaws.com', region_suffix].compact.join('.')
565
- end
566
-
567
548
  def agent_properties
568
549
  @agent_properties ||= options.fetch('agent', {})
569
550
  end
@@ -585,8 +566,12 @@ module Bosh::AwsCloud
585
566
  # @ec2.regions[] does not properly set the endpoint on the region (bug in /aws/ec2/region_collection.rb)
586
567
  # It just returns a Region object with nothing set but the name.
587
568
  # As a workaround use the 'each' method, which is implemented correctly
588
- @region = @ec2.regions.select {|r| r.name == aws_region}.first
589
- @az_selector = AvailabilityZoneSelector.new(@region, aws_properties['default_availability_zone'])
569
+ begin
570
+ @region = @ec2.regions.select {|r| r.name == aws_region}.first
571
+ @az_selector = AvailabilityZoneSelector.new(@region, aws_properties['default_availability_zone'])
572
+ rescue Net::OpenTimeout => e
573
+ cloud_error("Please make sure the CPI has proper network access to AWS. #{e.inspect}")
574
+ end
590
575
  end
591
576
 
592
577
  def initialize_registry
@@ -1,28 +1,25 @@
1
- # Copyright (c) 2009-2012 VMware, Inc.
2
-
3
1
  module Bosh::AwsCloud
4
2
 
5
3
  module Helpers
6
4
  def default_ephemeral_disk_mapping
7
- [
8
- {
9
- device_name: '/dev/sdb',
10
- virtual_name: 'ephemeral0',
11
- }
12
- ]
5
+ [
6
+ {
7
+ :device_name => '/dev/sdb',
8
+ :virtual_name => 'ephemeral0',
9
+ },
10
+ ]
13
11
  end
14
12
 
15
- def ebs_ephemeral_disk_mapping(volume_size_in_gb, volume_type)
16
- [
17
- {
18
- device_name: '/dev/sdb',
19
- ebs: {
20
- volume_size: volume_size_in_gb,
21
- volume_type: volume_type,
22
- delete_on_termination: true,
23
- },
24
- },
25
- ]
13
+ def ebs_ephemeral_disk_mapping(volume_size_in_gb, volume_type, iops = nil)
14
+ ebs = {
15
+ volume_size: volume_size_in_gb,
16
+ volume_type: volume_type,
17
+ delete_on_termination: true,
18
+ }
19
+
20
+ ebs[:iops] = iops if iops
21
+
22
+ [{device_name: '/dev/sdb', ebs: ebs}]
26
23
  end
27
24
 
28
25
  ##
@@ -7,6 +7,7 @@ module Bosh::AwsCloud
7
7
 
8
8
  class DiskInfo
9
9
  attr_reader :size, :count
10
+
10
11
  def initialize(size, count)
11
12
  @size = size
12
13
  @count = count
@@ -14,59 +15,59 @@ module Bosh::AwsCloud
14
15
  end
15
16
 
16
17
  InstanceStorageMap = {
17
- # previous generation
18
- 'm1.small' => DiskInfo.new(160, 1),
19
- 'm1.medium' => DiskInfo.new(410, 1),
20
- 'm1.large' => DiskInfo.new(420, 2),
21
- 'm1.xlarge' => DiskInfo.new(420, 4),
18
+ # previous generation
19
+ 'm1.small' => DiskInfo.new(160, 1),
20
+ 'm1.medium' => DiskInfo.new(410, 1),
21
+ 'm1.large' => DiskInfo.new(420, 2),
22
+ 'm1.xlarge' => DiskInfo.new(420, 4),
22
23
 
23
- 'c1.medium' => DiskInfo.new(350, 1),
24
- 'c1.xlarge' => DiskInfo.new(420, 4),
24
+ 'c1.medium' => DiskInfo.new(350, 1),
25
+ 'c1.xlarge' => DiskInfo.new(420, 4),
25
26
 
26
- 'cc2.8xlarge' => DiskInfo.new(840, 4),
27
+ 'cc2.8xlarge' => DiskInfo.new(840, 4),
27
28
 
28
- 'cg1.4xlarge' => DiskInfo.new(840, 2),
29
+ 'cg1.4xlarge' => DiskInfo.new(840, 2),
29
30
 
30
- 'm2.xlarge' => DiskInfo.new(420, 1),
31
- 'm2.2xlarge' => DiskInfo.new(850, 1),
32
- 'm2.4xlarge' => DiskInfo.new(840, 2),
31
+ 'm2.xlarge' => DiskInfo.new(420, 1),
32
+ 'm2.2xlarge' => DiskInfo.new(850, 1),
33
+ 'm2.4xlarge' => DiskInfo.new(840, 2),
33
34
 
34
- 'cr1.8xlarge' => DiskInfo.new(120, 2),
35
+ 'cr1.8xlarge' => DiskInfo.new(120, 2),
35
36
 
36
- 'hi1.4xlarge' => DiskInfo.new(1024, 2),
37
+ 'hi1.4xlarge' => DiskInfo.new(1024, 2),
37
38
 
38
- 'hs1.8xlarge' => DiskInfo.new(2000, 24),
39
+ 'hs1.8xlarge' => DiskInfo.new(2000, 24),
39
40
 
40
- # current generation
41
- 'm3.medium' => DiskInfo.new(4, 1),
42
- 'm3.large' => DiskInfo.new(32, 1),
43
- 'm3.xlarge' => DiskInfo.new(40, 2),
44
- 'm3.2xlarge' => DiskInfo.new(80, 2),
41
+ # current generation
42
+ 'm3.medium' => DiskInfo.new(4, 1),
43
+ 'm3.large' => DiskInfo.new(32, 1),
44
+ 'm3.xlarge' => DiskInfo.new(40, 2),
45
+ 'm3.2xlarge' => DiskInfo.new(80, 2),
45
46
 
46
- 'c3.large' => DiskInfo.new(16, 2),
47
- 'c3.xlarge' => DiskInfo.new(40, 2),
48
- 'c3.2xlarge' => DiskInfo.new(80, 2),
49
- 'c3.4xlarge' => DiskInfo.new(160, 2),
50
- 'c3.8xlarge' => DiskInfo.new(320, 2),
47
+ 'c3.large' => DiskInfo.new(16, 2),
48
+ 'c3.xlarge' => DiskInfo.new(40, 2),
49
+ 'c3.2xlarge' => DiskInfo.new(80, 2),
50
+ 'c3.4xlarge' => DiskInfo.new(160, 2),
51
+ 'c3.8xlarge' => DiskInfo.new(320, 2),
51
52
 
52
- 'r3.large' => DiskInfo.new(32, 1),
53
- 'r3.xlarge' => DiskInfo.new(80, 1),
54
- 'r3.2xlarge' => DiskInfo.new(160, 1),
55
- 'r3.4xlarge' => DiskInfo.new(320, 1),
56
- 'r3.8xlarge' => DiskInfo.new(320, 2),
53
+ 'r3.large' => DiskInfo.new(32, 1),
54
+ 'r3.xlarge' => DiskInfo.new(80, 1),
55
+ 'r3.2xlarge' => DiskInfo.new(160, 1),
56
+ 'r3.4xlarge' => DiskInfo.new(320, 1),
57
+ 'r3.8xlarge' => DiskInfo.new(320, 2),
57
58
 
58
- 'g2.2xlarge' => DiskInfo.new(60, 1),
59
- 'g2.8xlarge' => DiskInfo.new(120, 2),
59
+ 'g2.2xlarge' => DiskInfo.new(60, 1),
60
+ 'g2.8xlarge' => DiskInfo.new(120, 2),
60
61
 
61
- 'i2.xlarge' => DiskInfo.new(800, 1),
62
- 'i2.2xlarge' => DiskInfo.new(800, 2),
63
- 'i2.4xlarge' => DiskInfo.new(800, 4),
64
- 'i2.8xlarge' => DiskInfo.new(800, 8),
62
+ 'i2.xlarge' => DiskInfo.new(800, 1),
63
+ 'i2.2xlarge' => DiskInfo.new(800, 2),
64
+ 'i2.4xlarge' => DiskInfo.new(800, 4),
65
+ 'i2.8xlarge' => DiskInfo.new(800, 8),
65
66
 
66
- 'd2.xlarge' => DiskInfo.new(2000, 3),
67
- 'd2.2xlarge' => DiskInfo.new(2000, 6),
68
- 'd2.4xlarge' => DiskInfo.new(2000, 12),
69
- 'd2.8xlarge' => DiskInfo.new(2000, 24)
67
+ 'd2.xlarge' => DiskInfo.new(2000, 3),
68
+ 'd2.2xlarge' => DiskInfo.new(2000, 6),
69
+ 'd2.4xlarge' => DiskInfo.new(2000, 12),
70
+ 'd2.8xlarge' => DiskInfo.new(2000, 24)
70
71
  }
71
72
 
72
73
  def initialize(region, registry, elb, az_selector, logger)
@@ -82,7 +83,7 @@ module Bosh::AwsCloud
82
83
 
83
84
  @logger.info("Creating new instance with: #{instance_params.inspect}")
84
85
 
85
- aws_instance = create_aws_instance(instance_params, resource_pool["spot_bid_price"])
86
+ aws_instance = create_aws_instance(instance_params, resource_pool)
86
87
 
87
88
  instance = Instance.new(aws_instance, @registry, @elb, @logger)
88
89
 
@@ -102,9 +103,9 @@ module Bosh::AwsCloud
102
103
  end
103
104
 
104
105
  block_device_agent_info = block_device_info
105
- .group_by { |v| v[:bosh_type] }
106
- .map { |type, devices| {type => devices.map { |device| {"path" => device[:device_name] }}}}
107
- .inject({}) { |a, b| a.merge(b) }
106
+ .group_by { |v| v[:bosh_type] }
107
+ .map { |type, devices| {type => devices.map { |device| {"path" => device[:device_name]} }} }
108
+ .inject({}) { |a, b| a.merge(b) }
108
109
 
109
110
  return instance, block_device_agent_info
110
111
  end
@@ -123,7 +124,7 @@ module Bosh::AwsCloud
123
124
  instance_params = {count: 1}
124
125
  instance_params[:image_id] = stemcell_id
125
126
  instance_params[:instance_type] = resource_pool["instance_type"]
126
- instance_params[:block_device_mappings] = block_device_info.map { |entry| entry.reject{ |k| k == :bosh_type } }
127
+ instance_params[:block_device_mappings] = block_device_info.map { |entry| entry.reject { |k| k == :bosh_type } }
127
128
  instance_params[:placement_group] = resource_pool["placement_group"] if resource_pool["placement_group"]
128
129
  instance_params[:dedicated_tenancy] = true if resource_pool["tenancy"] == "dedicated"
129
130
 
@@ -142,46 +143,65 @@ module Bosh::AwsCloud
142
143
  return instance_params, block_device_info
143
144
  end
144
145
 
145
- def create_aws_instance(instance_params, spot_bid_price)
146
- if spot_bid_price
147
- @logger.info("Launching spot instance...")
148
- spot_manager = Bosh::AwsCloud::SpotManager.new(@region)
149
- spot_manager.create(instance_params, spot_bid_price)
150
- else
151
- # Retry the create instance operation a couple of times if we are told that the IP
152
- # address is in use - it can happen when the director recreates a VM and AWS
153
- # is too slow to update its state when we have released the IP address and want to
154
- # realocate it again.
155
- errors = [AWS::EC2::Errors::InvalidIPAddress::InUse, AWS::EC2::Errors::RequestLimitExceeded]
156
- Bosh::Common.retryable(sleep: instance_create_wait_time, tries: 20, on: errors) do |tries, error|
157
- @logger.info("Launching on demand instance...")
158
- @logger.warn("IP address was in use: #{error}") if tries > 0
159
- @region.instances.create(instance_params)
146
+ def create_aws_spot_instance(instance_params, spot_bid_price)
147
+ @logger.info("Launching spot instance...")
148
+ spot_manager = Bosh::AwsCloud::SpotManager.new(@region)
149
+
150
+ spot_manager.create(instance_params, spot_bid_price)
151
+ end
152
+
153
+ def create_aws_instance(instance_params, resource_pool)
154
+ if resource_pool["spot_bid_price"]
155
+ begin
156
+ return create_aws_spot_instance instance_params, resource_pool["spot_bid_price"]
157
+ rescue Bosh::Clouds::VMCreationFailed => e
158
+ raise unless resource_pool["spot_ondemand_fallback"]
160
159
  end
161
160
  end
161
+
162
+ # Retry the create instance operation a couple of times if we are told that the IP
163
+ # address is in use - it can happen when the director recreates a VM and AWS
164
+ # is too slow to update its state when we have released the IP address and want to
165
+ # realocate it again.
166
+ errors = [AWS::EC2::Errors::InvalidIPAddress::InUse, AWS::EC2::Errors::RequestLimitExceeded]
167
+ Bosh::Common.retryable(sleep: instance_create_wait_time, tries: 20, on: errors) do |tries, error|
168
+ @logger.info("Launching on demand instance...")
169
+ if error.class == AWS::EC2::Errors::InvalidIPAddress::InUse
170
+ @logger.warn("IP address was in use: #{error}")
171
+ end
172
+ @region.instances.create(instance_params)
173
+ end
162
174
  end
163
175
 
164
- def instance_create_wait_time; 30; end
176
+ def instance_create_wait_time
177
+ 30
178
+ end
165
179
 
166
180
  def block_device_mapping(virtualization_type, resource_pool)
167
181
  ephemeral_disk_options = resource_pool.fetch("ephemeral_disk", {})
168
182
 
169
- ephemeral_volume_size_in_mb = ephemeral_disk_options.fetch('size', 0)
170
- ephemeral_volume_size_in_gb = (ephemeral_volume_size_in_mb / 1024.0).ceil
171
- ephemeral_volume_type = ephemeral_disk_options.fetch('type', 'standard')
172
- instance_type = resource_pool.fetch('instance_type', '')
183
+ requested_size = ephemeral_disk_options['size'] || 0
184
+ actual_size = ephemeral_disk_options['size'] || 10 * 1024
185
+
186
+ ephemeral_volume_properties = VolumeProperties.new(
187
+ size: actual_size,
188
+ type: ephemeral_disk_options['type'],
189
+ iops: ephemeral_disk_options['iops'],
190
+ )
191
+
192
+ ephemeral_volume_properties.validate!
193
+
194
+ instance_type = resource_pool.fetch('instance_type', 'unspecified')
173
195
  raw_instance_storage = resource_pool.fetch('raw_instance_storage', false)
174
196
 
175
197
  local_disk_info = InstanceManager::InstanceStorageMap[instance_type]
176
198
  if raw_instance_storage && local_disk_info.nil?
177
- raise Bosh::Clouds::CloudError, "raw_instance_storage requested for instance type #{instance_type} that does not have instance storage"
199
+ raise Bosh::Clouds::CloudError, "raw_instance_storage requested for instance type '#{instance_type}' that does not have instance storage"
178
200
  end
179
201
 
180
- if raw_instance_storage || local_disk_info.nil? || local_disk_info.size < ephemeral_volume_size_in_gb
202
+ if raw_instance_storage || local_disk_info.nil? || local_disk_info.size < (requested_size / 1024.0).ceil
181
203
  @logger.debug('Use EBS storage to create the virtual machine')
182
-
183
- ephemeral_volume_size_in_gb = 10 if ephemeral_volume_size_in_gb == 0
184
- block_device_mapping_param = ebs_ephemeral_disk_mapping ephemeral_volume_size_in_gb, ephemeral_volume_type
204
+ block_device_mapping_param = InstancesCreatePresenter.new(ephemeral_volume_properties).present
185
205
  else
186
206
  @logger.debug('Use instance storage to create the virtual machine')
187
207
  block_device_mapping_param = default_ephemeral_disk_mapping
@@ -193,38 +213,44 @@ module Bosh::AwsCloud
193
213
  next_device = first_raw_ephemeral_device(virtualization_type)
194
214
  for i in 0..local_disk_info.count - 1 do
195
215
  block_device_mapping_param << {
196
- virtual_name: "ephemeral#{i}",
197
- device_name: next_device,
198
- bosh_type: "raw_ephemeral",
216
+ virtual_name: "ephemeral#{i}",
217
+ device_name: next_device,
218
+ bosh_type: "raw_ephemeral",
199
219
  }
200
220
  next_device = next_device.next
201
221
  end
202
222
  end
203
223
 
204
- if(resource_pool.has_key?("root_disk"))
224
+ if (resource_pool.has_key?('root_disk'))
225
+ root_disk_size_in_mb = resource_pool['root_disk']['size']
226
+ root_disk_type = resource_pool['root_disk'].fetch('type', 'standard')
227
+ root_disk_iops = resource_pool['root_disk']['iops']
228
+ root_disk_volume_properties = VolumeProperties.new(
229
+ size: root_disk_size_in_mb,
230
+ type: root_disk_type,
231
+ iops: root_disk_iops
232
+ )
233
+ root_disk_volume_properties.validate!
234
+
235
+ root_device = {
236
+ :volume_size => (root_disk_size_in_mb / 1024.0).ceil,
237
+ :volume_type => root_disk_type,
238
+ :delete_on_termination => true,
239
+ }
205
240
 
206
- if resource_pool["root_disk"]["size"].nil?
207
- raise Bosh::Clouds::CloudError, "root_disk block provided without size"
241
+ if root_disk_type == 'io1' && root_disk_iops > 0
242
+ root_device[:iops] = root_disk_iops
208
243
  end
209
244
 
210
- root_disk_size_in_mb = resource_pool["root_disk"]['size']
211
- root_disk_type = resource_pool["root_disk"].fetch('type', 'standard')
212
-
213
- root_device = {
214
- :volume_size => (root_disk_size_in_mb / 1024.0).ceil,
215
- :volume_type => root_disk_type,
216
- :delete_on_termination => true
217
- }
218
-
219
245
  if virtualization_type == :hvm
220
246
  block_device_mapping_param << {
221
- device_name: "/dev/xvda",
222
- ebs: root_device
247
+ device_name: "/dev/xvda",
248
+ ebs: root_device
223
249
  }
224
250
  else
225
251
  block_device_mapping_param << {
226
- device_name: "/dev/sda",
227
- ebs: root_device
252
+ device_name: "/dev/sda",
253
+ ebs: root_device
228
254
  }
229
255
  end
230
256
  end
@@ -0,0 +1,29 @@
1
+ module Bosh
2
+ module AwsCloud
3
+ class InstancesCreatePresenter
4
+ attr_reader :volume_properties
5
+ def initialize(volume_properties)
6
+ @volume_properties = volume_properties
7
+ end
8
+
9
+ def present
10
+ ebs = {
11
+ volume_size: volume_size_in_gb,
12
+ volume_type: volume_properties.type,
13
+ delete_on_termination: true,
14
+ }
15
+
16
+ ebs[:iops] = volume_properties.iops if volume_properties.iops
17
+
18
+ [{device_name: '/dev/sdb', ebs: ebs}]
19
+ end
20
+
21
+ private
22
+
23
+ def volume_size_in_gb
24
+ (volume_properties.size / 1024.0).ceil
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -12,6 +12,7 @@ module Bosh::AwsCloud
12
12
  end
13
13
 
14
14
  def create(instance_params, spot_bid_price)
15
+ @instance_params = instance_params
15
16
  spot_request_spec = create_spot_request_spec(instance_params, spot_bid_price)
16
17
  @logger.debug("Requesting spot instance with: #{spot_request_spec.inspect}")
17
18
 
@@ -22,12 +23,12 @@ module Bosh::AwsCloud
22
23
  raise Bosh::Clouds::VMCreationFailed.new(false), e.inspect
23
24
  end
24
25
 
25
- request_spot_instance
26
+ wait_for_spot_instance
26
27
  end
27
28
 
28
29
  private
29
30
 
30
- def request_spot_instance
31
+ def wait_for_spot_instance
31
32
  instance = nil
32
33
 
33
34
  # Query the spot request state until it becomes "active".
@@ -43,20 +44,17 @@ module Bosh::AwsCloud
43
44
  fail_spot_creation("VM spot instance creation failed: #{status.inspect}")
44
45
  when 'open'
45
46
  if status[:status] != nil && status[:status][:code] == 'price-too-low'
46
- fail_spot_creation("Cannot create VM spot instance because bid price is too low: #{status.inspect}")
47
+ fail_spot_creation("Cannot create VM spot instance because bid price is too low: #{status.inspect}. Reverting to creating ondemand instance")
47
48
  end
48
49
  when 'active'
49
50
  @logger.info("Spot request instances fulfilled: #{status.inspect}")
50
51
  instance = @region.instances[status[:instance_id]]
51
- true
52
52
  end
53
53
  end
54
54
 
55
55
  instance
56
56
  rescue Bosh::Common::RetryCountExceeded
57
- @logger.warn("Timed out waiting for spot request #{@spot_instance_requests.inspect} to be fulfilled")
58
- cancel_pending_spot_requests
59
- raise Bosh::Clouds::VMCreationFailed.new(true)
57
+ fail_spot_creation("Timed out waiting for spot request #{@spot_instance_requests.inspect} to be fulfilled. Reverting to creating ondemand instance")
60
58
  end
61
59
 
62
60
  def create_spot_request_spec(instance_params, spot_price)
@@ -103,7 +101,7 @@ module Bosh::AwsCloud
103
101
  end
104
102
 
105
103
  def fail_spot_creation(message)
106
- @logger.error(message)
104
+ @logger.warn(message)
107
105
  cancel_pending_spot_requests
108
106
  raise Bosh::Clouds::VMCreationFailed.new(false), message
109
107
  end
@@ -0,0 +1,44 @@
1
+ module Bosh
2
+ module AwsCloud
3
+ class VolumeProperties
4
+ include Helpers
5
+
6
+ attr_reader :size, :type, :iops, :az, :encrypted
7
+
8
+ def initialize(options)
9
+ @size = options[:size] || 0
10
+ @type = options[:type] || 'standard'
11
+ @iops = options[:iops]
12
+ @az = options[:az]
13
+ @encrypted = options[:encrypted] || false
14
+ end
15
+
16
+ def validate!
17
+ case @type
18
+ when 'standard'
19
+ cloud_error('AWS CPI minimum disk size is 1 GiB') if @size < 1024
20
+ cloud_error('AWS CPI maximum disk size is 1 TiB') if @size > 1024 * 1000
21
+ cloud_error("Cannot specify an 'iops' value when disk type is '#{@type}'. 'iops' is only allowed for 'io1' volume types.") unless @iops.nil?
22
+ when 'gp2'
23
+ cloud_error('AWS CPI minimum disk size is 1 GiB') if @size < 1024
24
+ cloud_error('AWS CPI maximum disk size is 16 TiB') if @size > 1024 * 16000
25
+ cloud_error("Cannot specify an 'iops' value when disk type is '#{@type}'. 'iops' is only allowed for 'io1' volume types.") unless @iops.nil?
26
+ when 'io1'
27
+ validate_iops
28
+ else
29
+ cloud_error("AWS CPI supports only gp2, io1, or standard disk type, received: #{@type}")
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def validate_iops
36
+ cloud_error("Must specify an 'iops' value when the volume type is 'io1'") if @iops.nil?
37
+ cloud_error('AWS CPI minimum disk size is 4 GiB') if @size < 1024 * 4
38
+ cloud_error('AWS CPI maximum disk size is 16 TiB') if @size > 1024 * 16000
39
+ cloud_error('AWS CPI maximum iops is 20000') if @iops >= 20000
40
+ cloud_error('AWS CPI maximum ratio iops/size is 30') if (@iops / (@size / 1024)).floor >= 30
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Bosh
2
+ module AwsCloud
3
+ class VolumesCreatePresenter
4
+ attr_reader :volume_properties
5
+ def initialize(volume_properties)
6
+ @volume_properties = volume_properties
7
+ end
8
+
9
+ def present
10
+ volume_options = {
11
+ size: (volume_properties.size / 1024.0).ceil,
12
+ availability_zone: volume_properties.az,
13
+ volume_type: volume_properties.type,
14
+ encrypted: volume_properties.encrypted
15
+ }
16
+ volume_options[:iops] = volume_properties.iops if volume_properties.iops
17
+ volume_options
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -16,7 +16,7 @@ IMAGE="$1"
16
16
  OUTPUT="$2"
17
17
 
18
18
  # workaround for issue on 12.04 LTS, use LANG=C
19
- echo ${IMAGE} | LANG=C egrep '^/[A-za-z0-9_/-]+/image$' > /dev/null 2>&1
19
+ echo ${IMAGE} | LANG=C egrep '^/[A-Za-z0-9_/-]+/image$' > /dev/null 2>&1
20
20
  if [ $? -ne 0 ]; then
21
21
  echo "ERROR: illegal image file: ${IMAGE}"
22
22
  exit 1
@@ -34,4 +34,4 @@ if [ ! -b ${OUTPUT} ]; then
34
34
  fi
35
35
 
36
36
  # copy image to block device with 1 MB block size
37
- tar -xzf ${IMAGE} -O root.img | dd bs=1M of=${OUTPUT}
37
+ tar -xzf ${IMAGE} -O root.img | dd bs=1M of=${OUTPUT}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh_aws_cpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - VMware
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-02 00:00:00.000000000 Z
11
+ date: 2016-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 2.4.0
75
+ version: 2.7.1
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 2.4.0
82
+ version: 2.7.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: yajl-ruby
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -102,7 +102,6 @@ executables:
102
102
  extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
- - README.md
106
105
  - bin/aws_cpi
107
106
  - bin/bosh_aws_console
108
107
  - lib/bosh_aws_cpi.rb
@@ -114,6 +113,7 @@ files:
114
113
  - lib/cloud/aws/helpers.rb
115
114
  - lib/cloud/aws/instance.rb
116
115
  - lib/cloud/aws/instance_manager.rb
116
+ - lib/cloud/aws/instances_create_presenter.rb
117
117
  - lib/cloud/aws/light_stemcell.rb
118
118
  - lib/cloud/aws/manual_network.rb
119
119
  - lib/cloud/aws/network.rb
@@ -125,6 +125,8 @@ files:
125
125
  - lib/cloud/aws/stemcell_finder.rb
126
126
  - lib/cloud/aws/tag_manager.rb
127
127
  - lib/cloud/aws/vip_network.rb
128
+ - lib/cloud/aws/volume_properties.rb
129
+ - lib/cloud/aws/volumes_create_presenter.rb
128
130
  - scripts/stemcell-copy.sh
129
131
  - scripts/vendor_gems
130
132
  homepage: https://github.com/cloudfoundry/bosh
data/README.md DELETED
@@ -1,125 +0,0 @@
1
- # BOSH AWS Cloud Provider Interface
2
- Copyright (c) 2009-2012 VMware, Inc.
3
-
4
- For online documentation see: http://rubydoc.info/gems/bosh_aws_cpi/
5
-
6
- ## Options
7
-
8
- These options are passed to the AWS CPI when it is instantiated.
9
-
10
- ### AWS options
11
-
12
- * `default_key_name` (required)
13
- default AWS ssh key name to assign to created virtual machines
14
- * `default_security_groups` (required)
15
- list of AWS security group names or ids to assign to created virtual machines, note that name and id can not be used together in this attribute.
16
- * `ec2_private_key` (required)
17
- local path to the ssh private key, must match `default_key_name`
18
- * `region` (required)
19
- EC2 region
20
- * `credentials_source` (optional)
21
- Where to get AWS credentials. This can be set to `static` for to use an `access_key_id` and `secret_access_key` or `env_or_profile` to get the credentials from environment variables or an EC2 instance profile. Defaults to `static` if not set
22
- * `access_key_id` (optional, required when `credentials_source` is `static`)
23
- AWS IAM user access key
24
- * `secret_access_key` (optional, required when `credentials_source` is `static`)
25
- AWS IAM secret access key
26
- * `iam_instance_profile` (optional)
27
- the [IAM Instance Profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-usingrole-instanceprofile.html) to use for the instance. This allows EC2 instances to use [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) when working with AWS APIs.
28
- * `ec2_endpoint` (optional)
29
- URL of the EC2 endpoint to connect to, defaults to the endpoint corresponding to the selected region,
30
- or `default_ec2_endpoint` if no region has been selected
31
- * `elb_endpoint` (optional)
32
- URL of the ELB endpoint to connect to, default to the endpoint corresponding to the selected region,
33
- or `default_elb_endpoint` if no region has been selected
34
- * `max_retries` (optional)
35
- maximum number of time to retry an AWS API call, defaults to `DEFAULT_MAX_RETRIES`
36
-
37
- ### Registry options
38
-
39
- The registry options are passed to the AWS CPI by the BOSH director based on the settings in `director.yml`, but can be
40
- overridden if needed.
41
-
42
- * `endpoint` (required)
43
- registry URL
44
- * `user` (required)
45
- registry user
46
- * `password` (required)
47
- registry password
48
-
49
- ### Agent options
50
-
51
- Agent options are passed to the AWS CPI by the BOSH director based on the settings in `director.yml`, but can be
52
- overridden if needed.
53
-
54
- ### Resource pool options
55
-
56
- These options are specified under `cloud_options` in the `resource_pools` section of a BOSH deployment manifest.
57
-
58
- * `availability_zone` (optional)
59
- the EC2 availability zone the VMs should be created in
60
- * `instance_type` (required)
61
- which [type of instance](http://aws.amazon.com/ec2/instance-types/) the VMs should belong to
62
- * `spot_bid_price` (optional)
63
- the [AWS spot instance](http://aws.amazon.com/ec2/purchasing-options/spot-instances/) bid price to use. When specified spot instances are started rather than on demand instances. _NB: this will dramatically slow down resource pool creation._
64
- * `iam_instance_profile` (optional)
65
- the [IAM Instance Profile](http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-usingrole-instanceprofile.html) to use for the instance. This allows EC2 instances to use [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) when working with AWS APIs.
66
- * `root_disk` (optional)
67
- * `size` (required)
68
- the size (in MB) of the root disk if a different size is needed than the default size built into the stemcell.
69
- * `type` (optional)
70
- the type of the root disk if a different type is needed than 'standard'
71
-
72
- ### Network options
73
-
74
- These options are specified under `cloud_options` in the `networks` section of a BOSH deployment manifest.
75
-
76
- * `type` (required)
77
- can be either `dynamic` for a DHCP assigned IP by AWS, or `vip` to use an Elastic IP (which needs to be already
78
- allocated)
79
-
80
- * `security_groups` (optional)
81
- the AWS security group names or ids to assign to VMs. If not specified, it'll use the default security groups set at the AWS options. Note that name and id can not be used together in this attribute.
82
-
83
- ## Example
84
-
85
- This is a sample of how AWS specific properties are used in a BOSH deployment manifest:
86
-
87
- ---
88
- name: sample
89
- director_uuid: 38ce80c3-e9e9-4aac-ba61-97c676631b91
90
-
91
- ...
92
-
93
- networks:
94
- - name: nginx_network
95
- type: vip
96
- cloud_properties: {}
97
- - name: default
98
- type: dynamic
99
- cloud_properties:
100
- security_groups:
101
- - default
102
-
103
- ...
104
-
105
- resource_pools:
106
- - name: common
107
- network: default
108
- size: 3
109
- stemcell:
110
- name: bosh-aws-xen-ubuntu
111
- version: latest
112
- cloud_properties:
113
- instance_type: m1.small
114
- root_disk:
115
- size: 11_264
116
- type: gp2
117
- ...
118
-
119
- properties:
120
- aws:
121
- access_key_id: AKIAIYJWVDUP4KRWBESQ
122
- secret_access_key: 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
123
- default_key_name: bosh
124
- default_security_groups: ["bosh"]
125
- ec2_private_key: /home/bosh/.ssh/bosh