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 +4 -4
- data/lib/cloud/aws.rb +3 -0
- data/lib/cloud/aws/availability_zone_selector.rb +2 -4
- data/lib/cloud/aws/cloud.rb +29 -44
- data/lib/cloud/aws/helpers.rb +16 -19
- data/lib/cloud/aws/instance_manager.rb +115 -89
- data/lib/cloud/aws/instances_create_presenter.rb +29 -0
- data/lib/cloud/aws/spot_manager.rb +6 -8
- data/lib/cloud/aws/volume_properties.rb +44 -0
- data/lib/cloud/aws/volumes_create_presenter.rb +22 -0
- data/scripts/stemcell-copy.sh +2 -2
- metadata +7 -5
- data/README.md +0 -125
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab03ce5aecf1e03ddb369df4a0906dda5b5d16fd
|
4
|
+
data.tar.gz: 673d0abaf1b22ea61797c997b70fdae0608ff24c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be78232ecfe2fb479b23ab635571050c9f33609e060dd0f63d45a78a2615b648e93161294afb652442ac6d40de049d6c3d33b23eeb3eb43ff973025bd05d452d
|
7
|
+
data.tar.gz: 5c86071d7c35194024548bab7d07c8e373b188ffe69dc7d7008584bac283d14625adbf865383faa0b8b2a5629c00e6f98d3d41a573dcf0a7d62e4f92c0d95de8
|
data/lib/cloud/aws.rb
CHANGED
@@ -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
|
-
|
14
|
-
|
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
|
data/lib/cloud/aws/cloud.rb
CHANGED
@@ -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
|
-
|
201
|
-
|
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
|
-
|
205
|
-
|
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
|
214
|
+
# Check whether an EBS volume exists or not
|
238
215
|
#
|
239
|
-
# @param [String] disk_id
|
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
|
-
|
589
|
-
|
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
|
data/lib/cloud/aws/helpers.rb
CHANGED
@@ -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
|
-
|
10
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
24
|
+
'c1.medium' => DiskInfo.new(350, 1),
|
25
|
+
'c1.xlarge' => DiskInfo.new(420, 4),
|
25
26
|
|
26
|
-
|
27
|
+
'cc2.8xlarge' => DiskInfo.new(840, 4),
|
27
28
|
|
28
|
-
|
29
|
+
'cg1.4xlarge' => DiskInfo.new(840, 2),
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
'm2.xlarge' => DiskInfo.new(420, 1),
|
32
|
+
'm2.2xlarge' => DiskInfo.new(850, 1),
|
33
|
+
'm2.4xlarge' => DiskInfo.new(840, 2),
|
33
34
|
|
34
|
-
|
35
|
+
'cr1.8xlarge' => DiskInfo.new(120, 2),
|
35
36
|
|
36
|
-
|
37
|
+
'hi1.4xlarge' => DiskInfo.new(1024, 2),
|
37
38
|
|
38
|
-
|
39
|
+
'hs1.8xlarge' => DiskInfo.new(2000, 24),
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
59
|
+
'g2.2xlarge' => DiskInfo.new(60, 1),
|
60
|
+
'g2.8xlarge' => DiskInfo.new(120, 2),
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
Bosh::
|
157
|
-
|
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
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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 <
|
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
|
-
|
197
|
-
|
198
|
-
|
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?(
|
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
|
207
|
-
|
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
|
-
|
222
|
-
|
247
|
+
device_name: "/dev/xvda",
|
248
|
+
ebs: root_device
|
223
249
|
}
|
224
250
|
else
|
225
251
|
block_device_mapping_param << {
|
226
|
-
|
227
|
-
|
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
|
-
|
26
|
+
wait_for_spot_instance
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
29
30
|
|
30
|
-
def
|
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
|
-
|
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.
|
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
|
+
|
data/scripts/stemcell-copy.sh
CHANGED
@@ -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-
|
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.
|
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:
|
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.
|
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.
|
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: EVGFswlmOvA33ZrU1ViFEtXC5Sugc19yPzokeWRf
|
123
|
-
default_key_name: bosh
|
124
|
-
default_security_groups: ["bosh"]
|
125
|
-
ec2_private_key: /home/bosh/.ssh/bosh
|