bosh_aws_cpi 0.3.3 → 0.5.0
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.
- data/bin/bosh_aws_console +72 -0
- data/lib/cloud/aws/cloud.rb +127 -104
- data/lib/cloud/aws/helpers.rb +38 -20
- data/lib/cloud/aws/version.rb +1 -1
- data/spec/assets/stemcell-copy +31 -0
- data/spec/integration/cpi_test.rb +78 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/unit/attach_disk_spec.rb +3 -6
- data/spec/unit/cloud_spec.rb +16 -0
- data/spec/unit/create_disk_spec.rb +3 -6
- data/spec/unit/create_stemcell_spec.rb +7 -18
- data/spec/unit/create_vm_spec.rb +67 -7
- data/spec/unit/delete_disk_spec.rb +2 -2
- data/spec/unit/delete_vm_spec.rb +1 -2
- data/spec/unit/detach_disk_spec.rb +1 -2
- data/spec/unit/helpers_spec.rb +5 -5
- data/spec/unit/reboot_vm_spec.rb +2 -4
- metadata +14 -8
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
4
|
+
|
5
|
+
# Usage example:
|
6
|
+
# irb(main):001:0> cpi.create_vm("test", "ami-809a48e9",
|
7
|
+
# {"instance_type" => "m1.small"}, {}, [], {"foo" =>"bar"})
|
8
|
+
|
9
|
+
gemfile = File.expand_path("../../Gemfile", __FILE__)
|
10
|
+
|
11
|
+
if File.exists?(gemfile)
|
12
|
+
ENV["BUNDLE_GEMFILE"] = gemfile
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
end
|
16
|
+
|
17
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
18
|
+
require "bosh_aws_cpi"
|
19
|
+
require "irb"
|
20
|
+
require "irb/completion"
|
21
|
+
require "ostruct"
|
22
|
+
require "optparse"
|
23
|
+
|
24
|
+
config_file = nil
|
25
|
+
|
26
|
+
opts_parser = OptionParser.new do |opts|
|
27
|
+
opts.on("-c", "--config FILE") { |file| config_file = file }
|
28
|
+
end
|
29
|
+
|
30
|
+
opts_parser.parse!
|
31
|
+
|
32
|
+
unless config_file
|
33
|
+
puts opts_parser
|
34
|
+
exit(1)
|
35
|
+
end
|
36
|
+
|
37
|
+
@config = YAML.load_file(config_file)
|
38
|
+
|
39
|
+
module ConsoleHelpers
|
40
|
+
def cpi
|
41
|
+
@cpi ||= Bosh::AwsCloud::Cloud.new(@config)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ec2
|
45
|
+
cpi.ec2
|
46
|
+
end
|
47
|
+
|
48
|
+
def registry
|
49
|
+
cpi.registry
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
cloud_config = OpenStruct.new(:logger => Logger.new(STDOUT))
|
54
|
+
|
55
|
+
Bosh::Clouds::Config.configure(cloud_config)
|
56
|
+
|
57
|
+
include ConsoleHelpers
|
58
|
+
|
59
|
+
begin
|
60
|
+
require "ruby-debug"
|
61
|
+
puts "=> Debugger enabled"
|
62
|
+
rescue LoadError
|
63
|
+
puts "=> ruby-debug not found, debugger disabled"
|
64
|
+
end
|
65
|
+
|
66
|
+
puts "=> Welcome to BOSH AWS CPI console"
|
67
|
+
puts "You can use 'cpi' to access CPI methods"
|
68
|
+
|
69
|
+
IRB.start
|
70
|
+
|
71
|
+
|
72
|
+
|
data/lib/cloud/aws/cloud.rb
CHANGED
@@ -11,13 +11,15 @@ module Bosh::AwsCloud
|
|
11
11
|
METADATA_TIMEOUT = 5 # seconds
|
12
12
|
DEVICE_POLL_TIMEOUT = 60 # seconds
|
13
13
|
|
14
|
-
DEFAULT_AKI = "aki-
|
14
|
+
DEFAULT_AKI = "aki-b4aa75dd"
|
15
|
+
DEFAULT_ROOT_DEVICE_NAME = "/dev/sda1"
|
15
16
|
|
16
17
|
# UBUNTU_10_04_32_BIT_US_EAST_EBS = "ami-3e9b4957"
|
17
18
|
# UBUNTU_10_04_32_BIT_US_EAST = "ami-809a48e9"
|
18
19
|
|
19
20
|
attr_reader :ec2
|
20
21
|
attr_reader :registry
|
22
|
+
attr_accessor :logger
|
21
23
|
|
22
24
|
##
|
23
25
|
# Initialize BOSH AWS CPI
|
@@ -94,15 +96,13 @@ module Bosh::AwsCloud
|
|
94
96
|
}
|
95
97
|
}
|
96
98
|
|
97
|
-
if disk_locality
|
98
|
-
# TODO: use as hint for availability zones
|
99
|
-
@logger.debug("Disk locality is ignored by AWS CPI")
|
100
|
-
end
|
101
|
-
|
102
99
|
security_groups =
|
103
100
|
network_configurator.security_groups(@default_security_groups)
|
104
101
|
@logger.debug("using security groups: #{security_groups.join(', ')}")
|
105
102
|
|
103
|
+
response = @ec2.client.describe_images(:image_ids => [stemcell_id])
|
104
|
+
root_device_name = response.images_set.first.root_device_name
|
105
|
+
|
106
106
|
instance_params = {
|
107
107
|
:image_id => stemcell_id,
|
108
108
|
:count => 1,
|
@@ -112,23 +112,20 @@ module Bosh::AwsCloud
|
|
112
112
|
:user_data => Yajl::Encoder.encode(user_data)
|
113
113
|
}
|
114
114
|
|
115
|
-
availability_zone =
|
116
|
-
|
117
|
-
|
118
|
-
end
|
115
|
+
instance_params[:availability_zone] =
|
116
|
+
select_availability_zone(disk_locality,
|
117
|
+
resource_pool["availability_zone"])
|
119
118
|
|
120
119
|
@logger.info("Creating new instance...")
|
121
120
|
instance = @ec2.instances.create(instance_params)
|
122
|
-
state = instance.status
|
123
121
|
|
124
|
-
@logger.info("Creating new instance `#{instance.id}'
|
125
|
-
|
126
|
-
|
127
|
-
wait_resource(instance, state, :running)
|
122
|
+
@logger.info("Creating new instance `#{instance.id}'")
|
123
|
+
wait_resource(instance, :running)
|
128
124
|
|
129
125
|
network_configurator.configure(@ec2, instance)
|
130
126
|
|
131
|
-
settings = initial_agent_settings(agent_id, network_spec, environment
|
127
|
+
settings = initial_agent_settings(agent_id, network_spec, environment,
|
128
|
+
root_device_name)
|
132
129
|
@registry.update_settings(instance.id, settings)
|
133
130
|
|
134
131
|
instance.id
|
@@ -137,22 +134,23 @@ module Bosh::AwsCloud
|
|
137
134
|
|
138
135
|
##
|
139
136
|
# Terminates EC2 instance and waits until it reports as terminated
|
140
|
-
# @param [String]
|
137
|
+
# @param [String] instance_id Running instance id
|
141
138
|
def delete_vm(instance_id)
|
142
139
|
with_thread_name("delete_vm(#{instance_id})") do
|
143
140
|
instance = @ec2.instances[instance_id]
|
144
141
|
|
145
142
|
instance.terminate
|
146
|
-
state = instance.status
|
147
|
-
|
148
|
-
# TODO: should this be done before or after deleting VM?
|
149
|
-
@logger.info("Deleting instance settings for `#{instance.id}'")
|
150
|
-
@registry.delete_settings(instance.id)
|
151
143
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
144
|
+
begin
|
145
|
+
# TODO: should this be done before or after deleting VM?
|
146
|
+
@logger.info("Deleting instance settings for `#{instance.id}'")
|
147
|
+
@registry.delete_settings(instance.id)
|
148
|
+
|
149
|
+
@logger.info("Deleting instance `#{instance.id}'")
|
150
|
+
wait_resource(instance, :terminated)
|
151
|
+
rescue AWS::EC2::Errors::InvalidInstanceID::NotFound
|
152
|
+
# It's OK, just means that instance has already been deleted
|
153
|
+
end
|
156
154
|
end
|
157
155
|
end
|
158
156
|
|
@@ -178,11 +176,11 @@ module Bosh::AwsCloud
|
|
178
176
|
raise ArgumentError, "disk size needs to be an integer"
|
179
177
|
end
|
180
178
|
|
181
|
-
if
|
179
|
+
if size < 1024
|
182
180
|
cloud_error("AWS CPI minimum disk size is 1 GiB")
|
183
181
|
end
|
184
182
|
|
185
|
-
if
|
183
|
+
if size > 1024 * 1000
|
186
184
|
cloud_error("AWS CPI maximum disk size is 1 TiB")
|
187
185
|
end
|
188
186
|
|
@@ -199,12 +197,8 @@ module Bosh::AwsCloud
|
|
199
197
|
}
|
200
198
|
|
201
199
|
volume = @ec2.volumes.create(volume_params)
|
202
|
-
|
203
|
-
|
204
|
-
@logger.info("Creating volume `#{volume.id}', " \
|
205
|
-
"state is `#{state}'")
|
206
|
-
|
207
|
-
wait_resource(volume, state, :available)
|
200
|
+
@logger.info("Creating volume `#{volume.id}'")
|
201
|
+
wait_resource(volume, :available)
|
208
202
|
|
209
203
|
volume.id
|
210
204
|
end
|
@@ -227,12 +221,10 @@ module Bosh::AwsCloud
|
|
227
221
|
volume.delete
|
228
222
|
|
229
223
|
begin
|
230
|
-
|
231
|
-
|
232
|
-
"state is `#{state}'")
|
233
|
-
|
234
|
-
wait_resource(volume, state, :deleted)
|
224
|
+
@logger.info("Deleting volume `#{volume.id}'")
|
225
|
+
wait_resource(volume, :deleted)
|
235
226
|
rescue AWS::EC2::Errors::InvalidVolume::NotFound
|
227
|
+
# It's OK, just means the volume has already been deleted
|
236
228
|
end
|
237
229
|
|
238
230
|
@logger.info("Volume `#{disk_id}' has been deleted")
|
@@ -310,32 +302,31 @@ module Bosh::AwsCloud
|
|
310
302
|
ebs_volume = find_ebs_device(sd_name)
|
311
303
|
|
312
304
|
# 2. Copy image to new EBS volume
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
}
|
305
|
+
@logger.info("Copying stemcell disk image to '#{ebs_volume}'")
|
306
|
+
copy_root_image(image_path, ebs_volume)
|
307
|
+
|
308
|
+
# 3. Create snapshot and then an image using this snapshot
|
309
|
+
snapshot = volume.create_snapshot
|
310
|
+
wait_resource(snapshot, :completed)
|
311
|
+
|
312
|
+
root_device_name = cloud_properties["root_device_name"] ||
|
313
|
+
DEFAULT_ROOT_DEVICE_NAME
|
314
|
+
|
315
|
+
image_params = {
|
316
|
+
:name => "BOSH-#{generate_unique_name}",
|
317
|
+
:architecture => "x86_64", # TODO should this be configurable?
|
318
|
+
:kernel_id => cloud_properties["kernel_id"] || DEFAULT_AKI,
|
319
|
+
:root_device_name => root_device_name,
|
320
|
+
:block_device_mappings => {
|
321
|
+
"/dev/sda" => { :snapshot_id => snapshot.id },
|
322
|
+
"/dev/sdb" => "ephemeral0"
|
332
323
|
}
|
324
|
+
}
|
333
325
|
|
334
|
-
|
335
|
-
|
326
|
+
image = @ec2.images.create(image_params)
|
327
|
+
wait_resource(image, :available, :state)
|
336
328
|
|
337
|
-
|
338
|
-
end
|
329
|
+
image.id
|
339
330
|
rescue => e
|
340
331
|
# TODO: delete snapshot?
|
341
332
|
@logger.error(e)
|
@@ -361,6 +352,32 @@ module Bosh::AwsCloud
|
|
361
352
|
not_implemented(:validate_deployment)
|
362
353
|
end
|
363
354
|
|
355
|
+
# Selects the availability zone to use from a list of disk volumes,
|
356
|
+
# resource pool availability zone (if any) and the default availability
|
357
|
+
# zone.
|
358
|
+
# @param [Hash] volumes volume ids to attach to the vm
|
359
|
+
# @param [String] resource_pool_az availability zone specified in
|
360
|
+
# the resource pool (may be nil)
|
361
|
+
# @return [String] availability zone to use
|
362
|
+
def select_availability_zone(volumes, resource_pool_az)
|
363
|
+
if volumes && !volumes.empty?
|
364
|
+
disks = volumes.map { |vid| @ec2.volumes[vid] }
|
365
|
+
ensure_same_availability_zone(disks, resource_pool_az)
|
366
|
+
disks.first.availability_zone
|
367
|
+
else
|
368
|
+
resource_pool_az || DEFAULT_AVAILABILITY_ZONE
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# ensure all supplied availability zones are the same
|
373
|
+
def ensure_same_availability_zone(disks, default)
|
374
|
+
zones = disks.map { |disk| disk.availability_zone }
|
375
|
+
zones << default if default
|
376
|
+
zones.uniq!
|
377
|
+
cloud_error "can't use multiple availability zones: %s" %
|
378
|
+
zones.join(", ") unless zones.size == 1 || zones.empty?
|
379
|
+
end
|
380
|
+
|
364
381
|
private
|
365
382
|
|
366
383
|
##
|
@@ -377,7 +394,8 @@ module Bosh::AwsCloud
|
|
377
394
|
# @param [Hash] network_spec Agent network spec
|
378
395
|
# @param [Hash] environment
|
379
396
|
# @return [Hash]
|
380
|
-
def initial_agent_settings(agent_id, network_spec, environment
|
397
|
+
def initial_agent_settings(agent_id, network_spec, environment,
|
398
|
+
root_device_name)
|
381
399
|
settings = {
|
382
400
|
"vm" => {
|
383
401
|
"name" => "vm-#{generate_unique_name}"
|
@@ -385,7 +403,7 @@ module Bosh::AwsCloud
|
|
385
403
|
"agent_id" => agent_id,
|
386
404
|
"networks" => network_spec,
|
387
405
|
"disks" => {
|
388
|
-
"system" =>
|
406
|
+
"system" => root_device_name,
|
389
407
|
"ephemeral" => "/dev/sdb",
|
390
408
|
"persistent" => {}
|
391
409
|
}
|
@@ -438,7 +456,9 @@ module Bosh::AwsCloud
|
|
438
456
|
end
|
439
457
|
|
440
458
|
def attach_ebs_volume(instance, volume)
|
441
|
-
|
459
|
+
# TODO once we upgrade the aws-sdk gem to > 1.3.9, we need to use:
|
460
|
+
# instance.block_device_mappings.to_hash.keys
|
461
|
+
device_names = Set.new(instance.block_device_mappings.to_hash.keys)
|
442
462
|
new_attachment = nil
|
443
463
|
|
444
464
|
("f".."p").each do |char| # f..p is what console suggests
|
@@ -458,12 +478,9 @@ module Bosh::AwsCloud
|
|
458
478
|
cloud_error("Instance has too many disks attached")
|
459
479
|
end
|
460
480
|
|
461
|
-
|
481
|
+
@logger.info("Attaching `#{volume.id}' to `#{instance.id}'")
|
482
|
+
wait_resource(new_attachment, :attached)
|
462
483
|
|
463
|
-
@logger.info("Attaching `#{volume.id}' to #{instance.id}, " \
|
464
|
-
"state is #{state}'")
|
465
|
-
|
466
|
-
wait_resource(new_attachment, state, :attached)
|
467
484
|
device_name = new_attachment.device
|
468
485
|
|
469
486
|
@logger.info("Attached `#{volume.id}' to `#{instance.id}', " \
|
@@ -473,7 +490,9 @@ module Bosh::AwsCloud
|
|
473
490
|
end
|
474
491
|
|
475
492
|
def detach_ebs_volume(instance, volume)
|
476
|
-
|
493
|
+
# TODO once we upgrade the aws-sdk gem to > 1.3.9, we need to use:
|
494
|
+
# instance.block_device_mappings.to_hash.keys
|
495
|
+
mappings = instance.block_device_mappings.to_hash
|
477
496
|
|
478
497
|
device_map = mappings.inject({}) do |hash, (device_name, attachment)|
|
479
498
|
hash[attachment.volume.id] = device_name
|
@@ -486,40 +505,51 @@ module Bosh::AwsCloud
|
|
486
505
|
end
|
487
506
|
|
488
507
|
attachment = volume.detach_from(instance, device_map[volume.id])
|
489
|
-
|
490
|
-
|
491
|
-
@logger.info("Detaching `#{volume.id}' from `#{instance.id}', " \
|
492
|
-
"state is #{state}'")
|
508
|
+
@logger.info("Detaching `#{volume.id}' from `#{instance.id}'")
|
493
509
|
|
494
510
|
begin
|
495
|
-
wait_resource(attachment,
|
511
|
+
wait_resource(attachment, :detached)
|
496
512
|
rescue AWS::Core::Resource::NotFound
|
497
|
-
# It's OK, just means attachment is gone
|
513
|
+
# It's OK, just means attachment is gone by now
|
498
514
|
end
|
499
515
|
end
|
500
516
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
517
|
+
# This method tries to execute the helper script stemcell-copy
|
518
|
+
# as root using sudo, since it needs to write to the ebs_volume.
|
519
|
+
# If stemcell-copy isn't available, it falls back to writing directly
|
520
|
+
# to the device, which is used in the micro bosh deployer.
|
521
|
+
# The stemcell-copy script must be in the PATH of the user running
|
522
|
+
# the director, and needs sudo privileges to execute without
|
523
|
+
# password.
|
524
|
+
def copy_root_image(image_path, ebs_volume)
|
525
|
+
path = ENV["PATH"]
|
526
|
+
|
527
|
+
if stemcell_copy = has_stemcell_copy(path)
|
528
|
+
@logger.debug("copying stemcell using stemcell-copy script")
|
529
|
+
# note that is is a potentially dangerous operation, but as the
|
530
|
+
# stemcell-copy script sets PATH to a sane value this is safe
|
531
|
+
out = `sudo #{stemcell_copy} #{image_path} #{ebs_volume} 2>&1`
|
532
|
+
else
|
533
|
+
@logger.info("falling back to using dd to copy stemcell")
|
534
|
+
out = `tar -xzf #{image_path} -O root.img | dd of=#{ebs_volume} 2>&1`
|
506
535
|
end
|
507
536
|
|
508
|
-
|
509
|
-
|
510
|
-
|
537
|
+
unless $?.exitstatus == 0
|
538
|
+
cloud_error("Unable to copy stemcell root image, " \
|
539
|
+
"exit status #{$?.exitstatus}: #{out}")
|
511
540
|
end
|
541
|
+
|
542
|
+
@logger.debug("stemcell copy output:\n#{out}")
|
512
543
|
end
|
513
544
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
"#{dd_out}")
|
521
|
-
end
|
545
|
+
# checks if the stemcell-copy script can be found in
|
546
|
+
# the current PATH
|
547
|
+
def has_stemcell_copy(path)
|
548
|
+
path.split(":").each do |dir|
|
549
|
+
stemcell_copy = File.join(dir, "stemcell-copy")
|
550
|
+
return stemcell_copy if File.exist?(stemcell_copy)
|
522
551
|
end
|
552
|
+
nil
|
523
553
|
end
|
524
554
|
|
525
555
|
def find_ebs_device(sd_name)
|
@@ -554,20 +584,13 @@ module Bosh::AwsCloud
|
|
554
584
|
# N.B. This will only work with ebs-store instances,
|
555
585
|
# as instance-store instances don't support stop/start.
|
556
586
|
instance.stop
|
557
|
-
state = instance.status
|
558
587
|
|
559
|
-
@logger.info("Stopping instance `#{instance.id}'
|
560
|
-
|
561
|
-
|
562
|
-
wait_resource(instance, state, :stopped)
|
588
|
+
@logger.info("Stopping instance `#{instance.id}'")
|
589
|
+
wait_resource(instance, :stopped)
|
563
590
|
|
564
591
|
instance.start
|
565
|
-
|
566
|
-
|
567
|
-
@logger.info("Starting instance `#{instance.id}', " \
|
568
|
-
"state is `#{state}'")
|
569
|
-
|
570
|
-
wait_resource(instance, state, :running)
|
592
|
+
@logger.info("Starting instance `#{instance.id}'")
|
593
|
+
wait_resource(instance, :running)
|
571
594
|
end
|
572
595
|
|
573
596
|
##
|
data/lib/cloud/aws/helpers.rb
CHANGED
@@ -4,7 +4,7 @@ module Bosh::AwsCloud
|
|
4
4
|
|
5
5
|
module Helpers
|
6
6
|
|
7
|
-
DEFAULT_TIMEOUT = 3600
|
7
|
+
DEFAULT_TIMEOUT = 3600 # seconds
|
8
8
|
|
9
9
|
##
|
10
10
|
# Raises CloudError exception
|
@@ -16,43 +16,61 @@ module Bosh::AwsCloud
|
|
16
16
|
raise Bosh::Clouds::CloudError, message
|
17
17
|
end
|
18
18
|
|
19
|
-
def wait_resource(resource,
|
20
|
-
target_state, state_method = :status,
|
19
|
+
def wait_resource(resource, target_state, state_method = :status,
|
21
20
|
timeout = DEFAULT_TIMEOUT)
|
22
21
|
|
23
22
|
started_at = Time.now
|
24
|
-
|
23
|
+
failures = 0
|
25
24
|
desc = resource.to_s
|
26
25
|
|
27
|
-
|
26
|
+
loop do
|
28
27
|
duration = Time.now - started_at
|
29
28
|
|
30
29
|
if duration > timeout
|
31
|
-
cloud_error("Timed out waiting for #{desc} "
|
32
|
-
"to be #{target_state}")
|
30
|
+
cloud_error("Timed out waiting for #{desc} to be #{target_state}")
|
33
31
|
end
|
34
32
|
|
35
33
|
if @logger
|
36
|
-
@logger.debug("Waiting for #{desc} " \
|
37
|
-
"
|
34
|
+
@logger.debug("Waiting for #{desc} to be #{target_state} " \
|
35
|
+
"(#{duration}s)")
|
38
36
|
end
|
39
37
|
|
40
|
-
|
38
|
+
begin
|
39
|
+
state = resource.send(state_method)
|
40
|
+
rescue AWS::EC2::Errors::InvalidAMIID::NotFound,
|
41
|
+
AWS::EC2::Errors::InvalidInstanceID::NotFound => e
|
42
|
+
# ugly workaround for AWS race conditions:
|
43
|
+
# 1) sometimes when we upload a stemcell and proceed to create a VM
|
44
|
+
# from it, AWS reports that the AMI is missing
|
45
|
+
# 2) sometimes when we create a new EC2 instance, AWS reports that
|
46
|
+
# the instance it returns is missing
|
47
|
+
# in both cases we just wait a little and retry...
|
48
|
+
raise e if failures > 3
|
49
|
+
failures += 1
|
50
|
+
@logger.error("#{e.message}: #{desc}")
|
51
|
+
sleep(1)
|
52
|
+
next
|
53
|
+
end
|
54
|
+
|
55
|
+
# This is not a very strong convention, but some resources
|
56
|
+
# have 'error' and 'failed' states, we probably don't want to keep
|
57
|
+
# waiting if we're in these states. Alternatively we could introduce a
|
58
|
+
# set of 'loop breaker' states but that doesn't seem very helpful
|
59
|
+
# at the moment
|
60
|
+
if state == :error || state == :failed
|
61
|
+
cloud_error("#{desc} state is #{state}, expected #{target_state}")
|
62
|
+
end
|
63
|
+
|
64
|
+
break if state == target_state
|
41
65
|
|
42
|
-
|
66
|
+
sleep(1)
|
43
67
|
end
|
44
68
|
|
45
|
-
if
|
46
|
-
|
47
|
-
|
48
|
-
"after #{Time.now - started_at}s")
|
49
|
-
end
|
50
|
-
else
|
51
|
-
cloud_error("#{desc} is #{state}, " \
|
52
|
-
"expected to be #{target_state}")
|
69
|
+
if @logger
|
70
|
+
total = Time.now - started_at
|
71
|
+
@logger.info("#{desc} is now #{target_state}, took #{total}s")
|
53
72
|
end
|
54
73
|
end
|
55
74
|
end
|
56
|
-
|
57
75
|
end
|
58
76
|
|
data/lib/cloud/aws/version.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# This script runs as root through sudo without the need for a password,
|
4
|
+
# so it needs to make sure it can't be abused.
|
5
|
+
#
|
6
|
+
|
7
|
+
# make sure we have a secure PATH
|
8
|
+
PATH=/bin:/usr/bin
|
9
|
+
export PATH
|
10
|
+
|
11
|
+
if [ $# -ne 1 ]; then
|
12
|
+
echo "usage: $0 <block device>"
|
13
|
+
exit 1
|
14
|
+
fi
|
15
|
+
|
16
|
+
OUTPUT="$1"
|
17
|
+
|
18
|
+
# TODO perhaps this should be more restrictive?
|
19
|
+
echo ${OUTPUT} | egrep '^/dev/[a-z0-9]+$' > /dev/null 2>&1
|
20
|
+
if [ $? -ne 0 ]; then
|
21
|
+
echo "ERROR: illegal device: ${OUTPUT}"
|
22
|
+
exit 1
|
23
|
+
fi
|
24
|
+
|
25
|
+
if [ ! -b ${OUTPUT} ]; then
|
26
|
+
echo "ERROR: missing device: ${OUTPUT}"
|
27
|
+
exit 1
|
28
|
+
fi
|
29
|
+
|
30
|
+
# copy image to block device with 1 MB block size
|
31
|
+
dd if=root.img if=${OUTPUT} bs=1M
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
4
|
+
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
describe Bosh::AwsCloud::Cloud do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
unless ENV["CPI_CONFIG_FILE"]
|
11
|
+
raise "Please provide CPI_CONFIG_FILE environment variable"
|
12
|
+
end
|
13
|
+
@config = YAML.load_file(ENV["CPI_CONFIG_FILE"])
|
14
|
+
@logger = Logger.new(STDOUT)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:cpi) do
|
18
|
+
cpi = Bosh::AwsCloud::Cloud.new(@config)
|
19
|
+
cpi.logger = @logger
|
20
|
+
|
21
|
+
# As we inject the configuration file from the outside, we don't care
|
22
|
+
# about spinning up the registry ourselves. However we don't want to bother
|
23
|
+
# EC2 at all if registry is not working, so just in case we perform a test
|
24
|
+
# health check against whatever has been provided.
|
25
|
+
cpi.registry.update_settings("foo", { "bar" => "baz" })
|
26
|
+
cpi.registry.read_settings("foo").should == { "bar" => "baz"}
|
27
|
+
|
28
|
+
cpi
|
29
|
+
end
|
30
|
+
|
31
|
+
it "exercises a VM lifecycle" do
|
32
|
+
instance_id = cpi.create_vm(
|
33
|
+
"agent-007", "ami-809a48e9",
|
34
|
+
{ "instance_type" => "m1.small" },
|
35
|
+
{ "default" => { "type" => "dynamic" }},
|
36
|
+
[], { "key" => "value" })
|
37
|
+
|
38
|
+
instance_id.should_not be_nil
|
39
|
+
|
40
|
+
settings = cpi.registry.read_settings(instance_id)
|
41
|
+
settings["vm"].should be_a(Hash)
|
42
|
+
settings["vm"]["name"].should_not be_nil
|
43
|
+
settings["agent_id"].should == "agent-007"
|
44
|
+
settings["networks"].should == { "default" => { "type" => "dynamic" }}
|
45
|
+
settings["disks"].should == {
|
46
|
+
"system" => "/dev/sda",
|
47
|
+
"ephemeral" => "/dev/sdb",
|
48
|
+
"persistent" => {}
|
49
|
+
}
|
50
|
+
|
51
|
+
settings["env"].should == { "key" => "value" }
|
52
|
+
|
53
|
+
volume_id = cpi.create_disk(2048)
|
54
|
+
volume_id.should_not be_nil
|
55
|
+
|
56
|
+
cpi.attach_disk(instance_id, volume_id)
|
57
|
+
settings = cpi.registry.read_settings(instance_id)
|
58
|
+
settings["disks"]["persistent"].should == { volume_id => "/dev/sdf" }
|
59
|
+
|
60
|
+
cpi.detach_disk(instance_id, volume_id)
|
61
|
+
settings = cpi.registry.read_settings(instance_id)
|
62
|
+
settings["disks"]["persistent"].should == {}
|
63
|
+
|
64
|
+
# TODO: test configure_networks (need an elastic IP at hand for that)
|
65
|
+
|
66
|
+
cpi.delete_vm(instance_id)
|
67
|
+
cpi.delete_disk(volume_id)
|
68
|
+
|
69
|
+
# Test below would fail: EC2 still reports the instance as 'terminated'
|
70
|
+
# for some time.
|
71
|
+
# cpi.ec2.instances[instance_id].should be_nil
|
72
|
+
|
73
|
+
expect {
|
74
|
+
cpi.registry.read_settings(instance_id)
|
75
|
+
}.to raise_error(/HTTP 404/)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -25,6 +25,20 @@ Bosh::Clouds::Config.configure(aws_config)
|
|
25
25
|
MOCK_AWS_ACCESS_KEY_ID = "foo"
|
26
26
|
MOCK_AWS_SECRET_ACCESS_KEY = "bar"
|
27
27
|
|
28
|
+
def internal_to(*args, &block)
|
29
|
+
example = describe *args, &block
|
30
|
+
klass = args[0]
|
31
|
+
if klass.is_a? Class
|
32
|
+
saved_private_instance_methods = klass.private_instance_methods
|
33
|
+
example.before do
|
34
|
+
klass.class_eval { public *saved_private_instance_methods }
|
35
|
+
end
|
36
|
+
example.after do
|
37
|
+
klass.class_eval { private *saved_private_instance_methods }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
28
42
|
def mock_cloud_options
|
29
43
|
{
|
30
44
|
"aws" => {
|
@@ -23,8 +23,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
23
23
|
|
24
24
|
instance.should_receive(:block_device_mappings).and_return({})
|
25
25
|
|
26
|
-
|
27
|
-
cloud.should_receive(:wait_resource).with(attachment, :attaching, :attached)
|
26
|
+
cloud.should_receive(:wait_resource).with(attachment, :attached)
|
28
27
|
|
29
28
|
old_settings = { "foo" => "bar" }
|
30
29
|
new_settings = {
|
@@ -61,8 +60,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
61
60
|
volume.should_receive(:attach_to).
|
62
61
|
with(instance, "/dev/sdh").and_return(attachment)
|
63
62
|
|
64
|
-
|
65
|
-
cloud.should_receive(:wait_resource).with(attachment, :attaching, :attached)
|
63
|
+
cloud.should_receive(:wait_resource).with(attachment, :attached)
|
66
64
|
|
67
65
|
old_settings = { "foo" => "bar" }
|
68
66
|
new_settings = {
|
@@ -99,8 +97,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
99
97
|
volume.should_receive(:attach_to).
|
100
98
|
with(instance, "/dev/sdh").and_return(attachment)
|
101
99
|
|
102
|
-
|
103
|
-
cloud.should_receive(:wait_resource).with(attachment, :attaching, :attached)
|
100
|
+
cloud.should_receive(:wait_resource).with(attachment, :attached)
|
104
101
|
|
105
102
|
old_settings = { "foo" => "bar" }
|
106
103
|
new_settings = {
|
data/spec/unit/cloud_spec.rb
CHANGED
@@ -13,4 +13,20 @@ describe Bosh::AwsCloud::Cloud do
|
|
13
13
|
|
14
14
|
end
|
15
15
|
|
16
|
+
internal_to Bosh::AwsCloud::Cloud do
|
17
|
+
|
18
|
+
it "should not find stemcell-copy" do
|
19
|
+
cloud = Bosh::Clouds::Provider.create(:aws, mock_cloud_options)
|
20
|
+
cloud.has_stemcell_copy("/usr/bin:/usr/sbin").should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should find stemcell-copy" do
|
24
|
+
cloud = Bosh::Clouds::Provider.create(:aws, mock_cloud_options)
|
25
|
+
path = ENV["PATH"]
|
26
|
+
path += ":#{File.expand_path('../../assets', __FILE__)}"
|
27
|
+
cloud.has_stemcell_copy(path).should_not be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
16
32
|
end
|
@@ -16,8 +16,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
16
16
|
ec2.volumes.should_receive(:create).with(disk_params).and_return(volume)
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
cloud.should_receive(:wait_resource).with(volume, :creating, :available)
|
19
|
+
cloud.should_receive(:wait_resource).with(volume, :available)
|
21
20
|
|
22
21
|
cloud.create_disk(2048).should == "v-foobar"
|
23
22
|
end
|
@@ -34,8 +33,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
34
33
|
ec2.volumes.should_receive(:create).with(disk_params).and_return(volume)
|
35
34
|
end
|
36
35
|
|
37
|
-
|
38
|
-
cloud.should_receive(:wait_resource).with(volume, :creating, :available)
|
36
|
+
cloud.should_receive(:wait_resource).with(volume, :available)
|
39
37
|
|
40
38
|
cloud.create_disk(2049)
|
41
39
|
end
|
@@ -67,8 +65,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
67
65
|
ec2.instances.stub(:[]).with("i-test").and_return(instance)
|
68
66
|
end
|
69
67
|
|
70
|
-
|
71
|
-
cloud.should_receive(:wait_resource).with(volume, :creating, :available)
|
68
|
+
cloud.should_receive(:wait_resource).with(volume, :available)
|
72
69
|
|
73
70
|
cloud.create_disk(1024, "i-test")
|
74
71
|
end
|
@@ -27,8 +27,8 @@ describe Bosh::AwsCloud::Cloud do
|
|
27
27
|
image_params = {
|
28
28
|
:name => "BOSH-#{unique_name}",
|
29
29
|
:architecture => "x86_64",
|
30
|
-
:kernel_id => "aki-
|
31
|
-
:root_device_name => "/dev/
|
30
|
+
:kernel_id => "aki-b4aa75dd",
|
31
|
+
:root_device_name => "/dev/sda1",
|
32
32
|
:block_device_mappings => {
|
33
33
|
"/dev/sda" => { :snapshot_id => "s-baz" },
|
34
34
|
"/dev/sdb" => "ephemeral0"
|
@@ -68,35 +68,24 @@ describe Bosh::AwsCloud::Cloud do
|
|
68
68
|
volume.should_receive(:attach_to).with(current_instance, "/dev/sdh").
|
69
69
|
and_return(attachment)
|
70
70
|
|
71
|
-
|
72
|
-
cloud.should_receive(:wait_resource).
|
73
|
-
with(attachment, :attaching, :attached)
|
71
|
+
cloud.should_receive(:wait_resource).with(attachment, :attached)
|
74
72
|
|
75
73
|
cloud.stub(:sleep)
|
76
74
|
|
77
75
|
File.stub(:blockdev?).with("/dev/sdh").and_return(false, false, false)
|
78
76
|
File.stub(:blockdev?).with("/dev/xvdh").and_return(false, false, true)
|
79
77
|
|
80
|
-
|
81
|
-
|
82
|
-
cloud.should_receive(:unpack_image).with(@tmp_dir, "/tmp/foo")
|
83
|
-
cloud.should_receive(:copy_root_image).with(@tmp_dir, "/dev/xvdh")
|
78
|
+
cloud.should_receive(:copy_root_image).with("/tmp/foo", "/dev/xvdh")
|
84
79
|
|
85
80
|
volume.should_receive(:create_snapshot).and_return(snapshot)
|
86
|
-
|
87
|
-
cloud.should_receive(:wait_resource).
|
88
|
-
with(snapshot, :in_progress, :completed)
|
81
|
+
cloud.should_receive(:wait_resource).with(snapshot, :completed)
|
89
82
|
|
90
|
-
|
91
|
-
cloud.should_receive(:wait_resource).with(image,
|
92
|
-
:creating, :available, :state)
|
83
|
+
cloud.should_receive(:wait_resource).with(image, :available, :state)
|
93
84
|
|
94
85
|
volume.should_receive(:detach_from).with(current_instance, "/dev/sdh").
|
95
86
|
and_return(attachment)
|
96
87
|
|
97
|
-
|
98
|
-
cloud.should_receive(:wait_resource).
|
99
|
-
with(attachment, :detaching, :detached)
|
88
|
+
cloud.should_receive(:wait_resource).with(attachment, :detached)
|
100
89
|
|
101
90
|
cloud.should_receive(:delete_disk).with("v-foo")
|
102
91
|
|
data/spec/unit/create_vm_spec.rb
CHANGED
@@ -12,7 +12,7 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
12
12
|
"agent_id" => "agent-id",
|
13
13
|
"networks" => { "network_a" => network_spec },
|
14
14
|
"disks" => {
|
15
|
-
"system" => "/dev/
|
15
|
+
"system" => "/dev/sda1",
|
16
16
|
"ephemeral" => "/dev/sdb",
|
17
17
|
"persistent" => {}
|
18
18
|
},
|
@@ -24,6 +24,11 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
27
|
+
def fake_image_set
|
28
|
+
image = double("image", :root_device_name => "/dev/sda1")
|
29
|
+
double("images_set", :images_set => [image])
|
30
|
+
end
|
31
|
+
|
27
32
|
def ec2_params(user_data, security_groups=[])
|
28
33
|
{
|
29
34
|
:image_id => "sc-id",
|
@@ -52,16 +57,17 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
52
57
|
instance = double("instance",
|
53
58
|
:id => "i-test",
|
54
59
|
:elastic_ip => nil)
|
60
|
+
client = double("client", :describe_images => fake_image_set)
|
55
61
|
|
56
62
|
cloud = mock_cloud do |ec2|
|
57
63
|
ec2.instances.should_receive(:create).
|
58
64
|
with(ec2_params(user_data)).
|
59
65
|
and_return(instance)
|
66
|
+
ec2.should_receive(:client).and_return(client)
|
60
67
|
end
|
61
68
|
|
62
|
-
instance.should_receive(:status).and_return(:pending)
|
63
69
|
cloud.should_receive(:generate_unique_name).and_return(unique_name)
|
64
|
-
cloud.should_receive(:wait_resource).with(instance, :
|
70
|
+
cloud.should_receive(:wait_resource).with(instance, :running)
|
65
71
|
@registry.should_receive(:update_settings)
|
66
72
|
.with("i-test", agent_settings(unique_name))
|
67
73
|
|
@@ -85,6 +91,7 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
85
91
|
instance = double("instance",
|
86
92
|
:id => "i-test",
|
87
93
|
:elastic_ip => nil)
|
94
|
+
client = double("client", :describe_images => fake_image_set)
|
88
95
|
|
89
96
|
security_groups = %w[foo bar]
|
90
97
|
network_spec = dynamic_network_spec
|
@@ -96,11 +103,11 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
96
103
|
ec2.instances.should_receive(:create).
|
97
104
|
with(ec2_params(user_data, security_groups)).
|
98
105
|
and_return(instance)
|
106
|
+
ec2.should_receive(:client).and_return(client)
|
99
107
|
end
|
100
108
|
|
101
|
-
instance.should_receive(:status).and_return(:pending)
|
102
109
|
cloud.should_receive(:generate_unique_name).and_return(unique_name)
|
103
|
-
cloud.should_receive(:wait_resource).with(instance, :
|
110
|
+
cloud.should_receive(:wait_resource).with(instance, :running)
|
104
111
|
@registry.should_receive(:update_settings)
|
105
112
|
.with("i-test", agent_settings(unique_name, network_spec))
|
106
113
|
|
@@ -116,14 +123,15 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
116
123
|
instance = double("instance",
|
117
124
|
:id => "i-test",
|
118
125
|
:elastic_ip => nil)
|
126
|
+
client = double("client", :describe_images => fake_image_set)
|
119
127
|
|
120
128
|
cloud = mock_cloud do |ec2|
|
121
129
|
ec2.instances.should_receive(:create).and_return(instance)
|
130
|
+
ec2.should_receive(:client).and_return(client)
|
122
131
|
end
|
123
132
|
|
124
|
-
instance.should_receive(:status).and_return(:pending)
|
125
133
|
instance.should_receive(:associate_elastic_ip).with("10.0.0.1")
|
126
|
-
cloud.should_receive(:wait_resource).with(instance, :
|
134
|
+
cloud.should_receive(:wait_resource).with(instance, :running)
|
127
135
|
@registry.should_receive(:update_settings)
|
128
136
|
|
129
137
|
vm_id = cloud.create_vm("agent-id", "sc-id",
|
@@ -131,4 +139,56 @@ describe Bosh::AwsCloud::Cloud, "create_vm" do
|
|
131
139
|
combined_network_spec)
|
132
140
|
end
|
133
141
|
|
142
|
+
def volume(zone)
|
143
|
+
vol = double("volume")
|
144
|
+
vol.stub(:availability_zone).and_return(zone)
|
145
|
+
vol
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "#select_availability_zone" do
|
149
|
+
|
150
|
+
it "should select the default availability_zone when all values are nil" do
|
151
|
+
cloud = mock_cloud
|
152
|
+
cloud.select_availability_zone(nil, nil).should ==
|
153
|
+
Bosh::AwsCloud::Cloud::DEFAULT_AVAILABILITY_ZONE
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should select the zone from a list of disks" do
|
157
|
+
cloud = mock_cloud do |ec2|
|
158
|
+
ec2.volumes.stub(:[]).and_return(volume("foo"), volume("foo"))
|
159
|
+
end
|
160
|
+
cloud.select_availability_zone(%w[cid1 cid2], nil).should == "foo"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should select the zone from a list of disks and a default" do
|
164
|
+
cloud = mock_cloud do |ec2|
|
165
|
+
ec2.volumes.stub(:[]).and_return(volume("foo"), volume("foo"))
|
166
|
+
end
|
167
|
+
cloud.select_availability_zone(%w[cid1 cid2], "foo").should == "foo"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "#ensure_same_availability_zone" do
|
172
|
+
it "should raise an error when the zones differ" do
|
173
|
+
cloud = mock_cloud
|
174
|
+
lambda {
|
175
|
+
cloud.ensure_same_availability_zone([volume("foo"), volume("bar")], nil)
|
176
|
+
}.should raise_error Bosh::Clouds::CloudError
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should raise an error when the zones differ" do
|
180
|
+
cloud = mock_cloud
|
181
|
+
lambda {
|
182
|
+
cloud.ensure_same_availability_zone([volume("foo"), volume("bar")], "foo")
|
183
|
+
}.should raise_error Bosh::Clouds::CloudError
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should raise an error when the zones differ" do
|
187
|
+
cloud = mock_cloud
|
188
|
+
lambda {
|
189
|
+
cloud.ensure_same_availability_zone([volume("foo"), volume("foo")], "bar")
|
190
|
+
}.should raise_error Bosh::Clouds::CloudError
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
134
194
|
end
|
@@ -11,9 +11,9 @@ describe Bosh::AwsCloud::Cloud do
|
|
11
11
|
ec2.volumes.stub(:[]).with("v-foo").and_return(volume)
|
12
12
|
end
|
13
13
|
|
14
|
-
volume.should_receive(:state).and_return(:available
|
14
|
+
volume.should_receive(:state).and_return(:available)
|
15
15
|
volume.should_receive(:delete)
|
16
|
-
cloud.should_receive(:wait_resource).with(volume, :
|
16
|
+
cloud.should_receive(:wait_resource).with(volume, :deleted)
|
17
17
|
|
18
18
|
cloud.delete_disk("v-foo")
|
19
19
|
end
|
data/spec/unit/delete_vm_spec.rb
CHANGED
@@ -16,8 +16,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
instance.should_receive(:terminate)
|
19
|
-
|
20
|
-
cloud.should_receive(:wait_resource).with(instance, :deleting, :terminated)
|
19
|
+
cloud.should_receive(:wait_resource).with(instance, :terminated)
|
21
20
|
|
22
21
|
@registry.should_receive(:delete_settings).with("i-foobar")
|
23
22
|
|
@@ -30,8 +30,7 @@ describe Bosh::AwsCloud::Cloud do
|
|
30
30
|
volume.should_receive(:detach_from).
|
31
31
|
with(instance, "/dev/sdf").and_return(attachment)
|
32
32
|
|
33
|
-
|
34
|
-
cloud.should_receive(:wait_resource).with(attachment, :detaching, :detached)
|
33
|
+
cloud.should_receive(:wait_resource).with(attachment, :detached)
|
35
34
|
|
36
35
|
old_settings = {
|
37
36
|
"foo" => "bar",
|
data/spec/unit/helpers_spec.rb
CHANGED
@@ -11,7 +11,7 @@ describe Bosh::AwsCloud::Helpers do
|
|
11
11
|
cloud.stub(:sleep)
|
12
12
|
|
13
13
|
lambda {
|
14
|
-
cloud.wait_resource(resource, :
|
14
|
+
cloud.wait_resource(resource, :stop, :status, 0.1)
|
15
15
|
}.should raise_error Bosh::Clouds::CloudError, /Timed out/
|
16
16
|
end
|
17
17
|
|
@@ -19,11 +19,11 @@ describe Bosh::AwsCloud::Helpers do
|
|
19
19
|
cloud = mock_cloud
|
20
20
|
|
21
21
|
resource = double("resource")
|
22
|
-
resource.stub(:status).and_return(:start, :stop)
|
22
|
+
resource.stub(:status).and_return(:start, :stopping, :stopping, :stop)
|
23
23
|
cloud.stub(:sleep)
|
24
24
|
|
25
25
|
lambda {
|
26
|
-
cloud.wait_resource(resource, :
|
26
|
+
cloud.wait_resource(resource, :stop, :status, 0.1)
|
27
27
|
}.should_not raise_error Bosh::Clouds::CloudError
|
28
28
|
end
|
29
29
|
|
@@ -35,8 +35,8 @@ describe Bosh::AwsCloud::Helpers do
|
|
35
35
|
cloud.stub(:sleep)
|
36
36
|
|
37
37
|
lambda {
|
38
|
-
cloud.wait_resource(resource, :
|
38
|
+
cloud.wait_resource(resource, :stopped, :status, 0.1)
|
39
39
|
}.should raise_error Bosh::Clouds::CloudError,
|
40
|
-
/is failed, expected
|
40
|
+
/is failed, expected stopped/
|
41
41
|
end
|
42
42
|
end
|
data/spec/unit/reboot_vm_spec.rb
CHANGED
@@ -25,14 +25,12 @@ describe Bosh::AwsCloud::Cloud do
|
|
25
25
|
it "hard reboots an EC2 instance" do
|
26
26
|
# N.B. This requires ebs-store instance
|
27
27
|
@instance.should_receive(:stop).ordered
|
28
|
-
@instance.should_receive(:status).ordered.and_return(:stopping)
|
29
28
|
@cloud.should_receive(:wait_resource).
|
30
|
-
with(@instance, :
|
29
|
+
with(@instance, :stopped).ordered
|
31
30
|
|
32
31
|
@instance.should_receive(:start)
|
33
|
-
@instance.should_receive(:status).and_return(:starting)
|
34
32
|
@cloud.should_receive(:wait_resource).ordered.
|
35
|
-
with(@instance, :
|
33
|
+
with(@instance, :running)
|
36
34
|
|
37
35
|
@cloud.send(:hard_reboot, @instance)
|
38
36
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bosh_aws_cpi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0.4.
|
53
|
+
version: 0.4.3
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,7 +58,7 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.4.
|
61
|
+
version: 0.4.3
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: httpclient
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,10 +109,12 @@ dependencies:
|
|
109
109
|
version: 0.8.2
|
110
110
|
description: BOSH AWS CPI
|
111
111
|
email: support@vmware.com
|
112
|
-
executables:
|
112
|
+
executables:
|
113
|
+
- bosh_aws_console
|
113
114
|
extensions: []
|
114
115
|
extra_rdoc_files: []
|
115
116
|
files:
|
117
|
+
- bin/bosh_aws_console
|
116
118
|
- lib/bosh_aws_cpi.rb
|
117
119
|
- lib/cloud/aws.rb
|
118
120
|
- lib/cloud/aws/cloud.rb
|
@@ -125,6 +127,8 @@ files:
|
|
125
127
|
- lib/cloud/aws/vip_network.rb
|
126
128
|
- README
|
127
129
|
- Rakefile
|
130
|
+
- spec/assets/stemcell-copy
|
131
|
+
- spec/integration/cpi_test.rb
|
128
132
|
- spec/spec_helper.rb
|
129
133
|
- spec/unit/attach_disk_spec.rb
|
130
134
|
- spec/unit/cloud_spec.rb
|
@@ -154,7 +158,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
154
158
|
version: '0'
|
155
159
|
segments:
|
156
160
|
- 0
|
157
|
-
hash:
|
161
|
+
hash: -2403392629027305829
|
158
162
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
163
|
none: false
|
160
164
|
requirements:
|
@@ -163,14 +167,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
167
|
version: '0'
|
164
168
|
segments:
|
165
169
|
- 0
|
166
|
-
hash:
|
170
|
+
hash: -2403392629027305829
|
167
171
|
requirements: []
|
168
172
|
rubyforge_project:
|
169
|
-
rubygems_version: 1.8.
|
173
|
+
rubygems_version: 1.8.24
|
170
174
|
signing_key:
|
171
175
|
specification_version: 3
|
172
176
|
summary: BOSH AWS CPI
|
173
177
|
test_files:
|
178
|
+
- spec/assets/stemcell-copy
|
179
|
+
- spec/integration/cpi_test.rb
|
174
180
|
- spec/spec_helper.rb
|
175
181
|
- spec/unit/attach_disk_spec.rb
|
176
182
|
- spec/unit/cloud_spec.rb
|