bosh_aws_cpi 0.3.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|