github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/ec2/ebs_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package ec2_test 5 6 import ( 7 "fmt" 8 "sort" 9 "strconv" 10 "time" 11 12 "github.com/juju/errors" 13 "github.com/juju/names" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils/arch" 16 "github.com/juju/utils/series" 17 awsec2 "gopkg.in/amz.v3/ec2" 18 "gopkg.in/amz.v3/ec2/ec2test" 19 gc "gopkg.in/check.v1" 20 21 "github.com/juju/juju/cloud" 22 "github.com/juju/juju/constraints" 23 "github.com/juju/juju/environs/config" 24 "github.com/juju/juju/environs/imagemetadata" 25 imagetesting "github.com/juju/juju/environs/imagemetadata/testing" 26 "github.com/juju/juju/environs/jujutest" 27 sstesting "github.com/juju/juju/environs/simplestreams/testing" 28 "github.com/juju/juju/environs/tags" 29 "github.com/juju/juju/instance" 30 "github.com/juju/juju/juju" 31 "github.com/juju/juju/provider/ec2" 32 "github.com/juju/juju/storage" 33 "github.com/juju/juju/testing" 34 jujuversion "github.com/juju/juju/version" 35 ) 36 37 type storageSuite struct { 38 testing.BaseSuite 39 } 40 41 var _ = gc.Suite(&storageSuite{}) 42 43 func (*storageSuite) TestValidateConfigUnknownConfig(c *gc.C) { 44 p := ec2.EBSProvider() 45 cfg, err := storage.NewConfig("foo", ec2.EBS_ProviderType, map[string]interface{}{ 46 "unknown": "config", 47 }) 48 c.Assert(err, jc.ErrorIsNil) 49 err = p.ValidateConfig(cfg) 50 c.Assert(err, jc.ErrorIsNil) // unknown attrs ignored 51 } 52 53 func (s *storageSuite) TestSupports(c *gc.C) { 54 p := ec2.EBSProvider() 55 c.Assert(p.Supports(storage.StorageKindBlock), jc.IsTrue) 56 c.Assert(p.Supports(storage.StorageKindFilesystem), jc.IsFalse) 57 } 58 59 var _ = gc.Suite(&ebsVolumeSuite{}) 60 61 type ebsVolumeSuite struct { 62 testing.BaseSuite 63 // TODO(axw) the EBS tests should not be embedding jujutest.Tests. 64 jujutest.Tests 65 srv localServer 66 restoreEC2Patching func() 67 68 instanceId string 69 } 70 71 func (s *ebsVolumeSuite) SetUpSuite(c *gc.C) { 72 s.BaseSuite.SetUpSuite(c) 73 s.Tests.SetUpSuite(c) 74 s.Credential = cloud.NewCredential( 75 cloud.AccessKeyAuthType, 76 map[string]string{ 77 "access-key": "x", 78 "secret-key": "x", 79 }, 80 ) 81 s.CloudRegion = "test" 82 83 // Upload arches that ec2 supports; add to this 84 // as ec2 coverage expands. 85 s.UploadArches = []string{arch.AMD64, arch.I386} 86 s.TestConfig = localConfigAttrs.Merge(testing.Attrs{ 87 "access-key": "x", 88 "secret-key": "x", 89 "region": "test", 90 }) 91 s.restoreEC2Patching = patchEC2ForTesting(c) 92 s.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 93 s.BaseSuite.PatchValue(&juju.JujuPublicKey, sstesting.SignedMetadataPublicKey) 94 imagetesting.PatchOfficialDataSources(&s.BaseSuite.CleanupSuite, "test:") 95 s.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 96 } 97 98 func (s *ebsVolumeSuite) TearDownSuite(c *gc.C) { 99 s.restoreEC2Patching() 100 s.Tests.TearDownSuite(c) 101 s.BaseSuite.TearDownSuite(c) 102 } 103 104 func (s *ebsVolumeSuite) SetUpTest(c *gc.C) { 105 s.BaseSuite.SetUpTest(c) 106 s.BaseSuite.PatchValue(&jujuversion.Current, testing.FakeVersionNumber) 107 s.BaseSuite.PatchValue(&arch.HostArch, func() string { return arch.AMD64 }) 108 s.BaseSuite.PatchValue(&series.HostSeries, func() string { return testing.FakeDefaultSeries }) 109 s.srv.startServer(c) 110 s.Tests.SetUpTest(c) 111 s.PatchValue(&ec2.DestroyVolumeAttempt.Delay, time.Duration(0)) 112 } 113 114 func (s *ebsVolumeSuite) TearDownTest(c *gc.C) { 115 s.Tests.TearDownTest(c) 116 s.srv.stopServer(c) 117 s.BaseSuite.TearDownTest(c) 118 } 119 120 func (s *ebsVolumeSuite) volumeSource(c *gc.C, cfg *storage.Config) storage.VolumeSource { 121 envCfg, err := config.New(config.NoDefaults, s.TestConfig) 122 c.Assert(err, jc.ErrorIsNil) 123 p := ec2.EBSProvider() 124 vs, err := p.VolumeSource(envCfg, cfg) 125 c.Assert(err, jc.ErrorIsNil) 126 return vs 127 } 128 129 func (s *ebsVolumeSuite) createVolumes(vs storage.VolumeSource, instanceId string) ([]storage.CreateVolumesResult, error) { 130 if instanceId == "" { 131 instanceId = s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Running, nil)[0] 132 } 133 volume0 := names.NewVolumeTag("0") 134 volume1 := names.NewVolumeTag("1") 135 volume2 := names.NewVolumeTag("2") 136 params := []storage.VolumeParams{{ 137 Tag: volume0, 138 Size: 10 * 1000, 139 Provider: ec2.EBS_ProviderType, 140 Attributes: map[string]interface{}{ 141 "volume-type": "io1", 142 "iops": 30, 143 }, 144 Attachment: &storage.VolumeAttachmentParams{ 145 AttachmentParams: storage.AttachmentParams{ 146 InstanceId: instance.Id(instanceId), 147 }, 148 }, 149 ResourceTags: map[string]string{ 150 tags.JujuModel: s.TestConfig["uuid"].(string), 151 }, 152 }, { 153 Tag: volume1, 154 Size: 20 * 1000, 155 Provider: ec2.EBS_ProviderType, 156 Attachment: &storage.VolumeAttachmentParams{ 157 AttachmentParams: storage.AttachmentParams{ 158 InstanceId: instance.Id(instanceId), 159 }, 160 }, 161 ResourceTags: map[string]string{ 162 tags.JujuModel: "something-else", 163 }, 164 }, { 165 Tag: volume2, 166 Size: 30 * 1000, 167 Provider: ec2.EBS_ProviderType, 168 ResourceTags: map[string]string{ 169 "abc": "123", 170 }, 171 Attachment: &storage.VolumeAttachmentParams{ 172 AttachmentParams: storage.AttachmentParams{ 173 InstanceId: instance.Id(instanceId), 174 }, 175 }, 176 }} 177 return vs.CreateVolumes(params) 178 } 179 180 func (s *ebsVolumeSuite) assertCreateVolumes(c *gc.C, vs storage.VolumeSource, instanceId string) { 181 results, err := s.createVolumes(vs, instanceId) 182 c.Assert(err, jc.ErrorIsNil) 183 c.Assert(results, gc.HasLen, 3) 184 c.Assert(results[0].Volume, jc.DeepEquals, &storage.Volume{ 185 names.NewVolumeTag("0"), 186 storage.VolumeInfo{ 187 Size: 10240, 188 VolumeId: "vol-0", 189 Persistent: true, 190 }, 191 }) 192 c.Assert(results[1].Volume, jc.DeepEquals, &storage.Volume{ 193 names.NewVolumeTag("1"), 194 storage.VolumeInfo{ 195 Size: 20480, 196 VolumeId: "vol-1", 197 Persistent: true, 198 }, 199 }) 200 c.Assert(results[2].Volume, jc.DeepEquals, &storage.Volume{ 201 names.NewVolumeTag("2"), 202 storage.VolumeInfo{ 203 Size: 30720, 204 VolumeId: "vol-2", 205 Persistent: true, 206 }, 207 }) 208 ec2Client := ec2.StorageEC2(vs) 209 ec2Vols, err := ec2Client.Volumes(nil, nil) 210 c.Assert(err, jc.ErrorIsNil) 211 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 212 sortBySize(ec2Vols.Volumes) 213 c.Assert(ec2Vols.Volumes[0].Size, gc.Equals, 10) 214 c.Assert(ec2Vols.Volumes[1].Size, gc.Equals, 20) 215 c.Assert(ec2Vols.Volumes[2].Size, gc.Equals, 30) 216 } 217 218 var deleteSecurityGroupForTestFunc = func(inst ec2.SecurityGroupCleaner, group awsec2.SecurityGroup) error { 219 // With an exponential retry for deleting security groups, 220 // we never return from local live tests. 221 // No need to re-try in tests anyway - just call delete. 222 _, err := inst.DeleteSecurityGroup(group) 223 return err 224 } 225 226 type volumeSorter struct { 227 vols []awsec2.Volume 228 less func(i, j awsec2.Volume) bool 229 } 230 231 func sortBySize(vols []awsec2.Volume) { 232 sort.Sort(volumeSorter{vols, func(i, j awsec2.Volume) bool { 233 return i.Size < j.Size 234 }}) 235 } 236 237 func (s volumeSorter) Len() int { 238 return len(s.vols) 239 } 240 241 func (s volumeSorter) Swap(i, j int) { 242 s.vols[i], s.vols[j] = s.vols[j], s.vols[i] 243 } 244 245 func (s volumeSorter) Less(i, j int) bool { 246 return s.less(s.vols[i], s.vols[j]) 247 } 248 249 func (s *ebsVolumeSuite) TestCreateVolumes(c *gc.C) { 250 vs := s.volumeSource(c, nil) 251 s.assertCreateVolumes(c, vs, "") 252 } 253 254 func (s *ebsVolumeSuite) TestVolumeTags(c *gc.C) { 255 vs := s.volumeSource(c, nil) 256 results, err := s.createVolumes(vs, "") 257 c.Assert(err, jc.ErrorIsNil) 258 c.Assert(results, gc.HasLen, 3) 259 c.Assert(results[0].Error, jc.ErrorIsNil) 260 c.Assert(results[0].Volume, jc.DeepEquals, &storage.Volume{ 261 names.NewVolumeTag("0"), 262 storage.VolumeInfo{ 263 Size: 10240, 264 VolumeId: "vol-0", 265 Persistent: true, 266 }, 267 }) 268 c.Assert(results[1].Error, jc.ErrorIsNil) 269 c.Assert(results[1].Volume, jc.DeepEquals, &storage.Volume{ 270 names.NewVolumeTag("1"), 271 storage.VolumeInfo{ 272 Size: 20480, 273 VolumeId: "vol-1", 274 Persistent: true, 275 }, 276 }) 277 c.Assert(results[2].Error, jc.ErrorIsNil) 278 c.Assert(results[2].Volume, jc.DeepEquals, &storage.Volume{ 279 names.NewVolumeTag("2"), 280 storage.VolumeInfo{ 281 Size: 30720, 282 VolumeId: "vol-2", 283 Persistent: true, 284 }, 285 }) 286 ec2Client := ec2.StorageEC2(vs) 287 ec2Vols, err := ec2Client.Volumes(nil, nil) 288 c.Assert(err, jc.ErrorIsNil) 289 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 290 sortBySize(ec2Vols.Volumes) 291 c.Assert(ec2Vols.Volumes[0].Tags, jc.SameContents, []awsec2.Tag{ 292 {"juju-model-uuid", "deadbeef-0bad-400d-8000-4b1d0d06f00d"}, 293 {"Name", "juju-sample-volume-0"}, 294 }) 295 c.Assert(ec2Vols.Volumes[1].Tags, jc.SameContents, []awsec2.Tag{ 296 {"juju-model-uuid", "something-else"}, 297 {"Name", "juju-sample-volume-1"}, 298 }) 299 c.Assert(ec2Vols.Volumes[2].Tags, jc.SameContents, []awsec2.Tag{ 300 {"Name", "juju-sample-volume-2"}, 301 {"abc", "123"}, 302 }) 303 } 304 305 func (s *ebsVolumeSuite) TestVolumeTypeAliases(c *gc.C) { 306 instanceIdRunning := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Running, nil)[0] 307 vs := s.volumeSource(c, nil) 308 ec2Client := ec2.StorageEC2(vs) 309 aliases := [][2]string{ 310 {"magnetic", "standard"}, 311 {"ssd", "gp2"}, 312 {"provisioned-iops", "io1"}, 313 } 314 for i, alias := range aliases { 315 params := []storage.VolumeParams{{ 316 Tag: names.NewVolumeTag("0"), 317 Size: 10 * 1000, 318 Provider: ec2.EBS_ProviderType, 319 Attributes: map[string]interface{}{ 320 "volume-type": alias[0], 321 }, 322 Attachment: &storage.VolumeAttachmentParams{ 323 AttachmentParams: storage.AttachmentParams{ 324 InstanceId: instance.Id(instanceIdRunning), 325 }, 326 }, 327 }} 328 if alias[1] == "io1" { 329 params[0].Attributes["iops"] = 30 330 } 331 results, err := vs.CreateVolumes(params) 332 c.Assert(err, jc.ErrorIsNil) 333 c.Assert(results, gc.HasLen, 1) 334 c.Assert(results[0].Error, jc.ErrorIsNil) 335 c.Assert(results[0].Volume.VolumeId, gc.Equals, fmt.Sprintf("vol-%d", i)) 336 } 337 ec2Vols, err := ec2Client.Volumes(nil, nil) 338 c.Assert(err, jc.ErrorIsNil) 339 c.Assert(ec2Vols.Volumes, gc.HasLen, len(aliases)) 340 sort.Sort(volumeSorter{ec2Vols.Volumes, func(i, j awsec2.Volume) bool { 341 return i.Id < j.Id 342 }}) 343 for i, alias := range aliases { 344 c.Assert(ec2Vols.Volumes[i].VolumeType, gc.Equals, alias[1]) 345 } 346 } 347 348 func (s *ebsVolumeSuite) TestDestroyVolumesNotFoundReturnsNil(c *gc.C) { 349 vs := s.volumeSource(c, nil) 350 results, err := vs.DestroyVolumes([]string{"vol-42"}) 351 c.Assert(err, jc.ErrorIsNil) 352 c.Assert(results, gc.HasLen, 1) 353 c.Assert(results[0], jc.ErrorIsNil) 354 } 355 356 func (s *ebsVolumeSuite) TestDestroyVolumes(c *gc.C) { 357 vs := s.volumeSource(c, nil) 358 params := s.setupAttachVolumesTest(c, vs, ec2test.Running) 359 errs, err := vs.DetachVolumes(params) 360 c.Assert(err, jc.ErrorIsNil) 361 c.Assert(errs, jc.DeepEquals, []error{nil}) 362 errs, err = vs.DestroyVolumes([]string{"vol-0"}) 363 c.Assert(err, jc.ErrorIsNil) 364 c.Assert(errs, jc.DeepEquals, []error{nil}) 365 366 ec2Client := ec2.StorageEC2(vs) 367 ec2Vols, err := ec2Client.Volumes(nil, nil) 368 c.Assert(err, jc.ErrorIsNil) 369 c.Assert(ec2Vols.Volumes, gc.HasLen, 2) 370 sortBySize(ec2Vols.Volumes) 371 c.Assert(ec2Vols.Volumes[0].Size, gc.Equals, 20) 372 } 373 374 func (s *ebsVolumeSuite) TestDestroyVolumesStillAttached(c *gc.C) { 375 vs := s.volumeSource(c, nil) 376 s.setupAttachVolumesTest(c, vs, ec2test.Running) 377 errs, err := vs.DestroyVolumes([]string{"vol-0"}) 378 c.Assert(err, jc.ErrorIsNil) 379 c.Assert(errs, jc.DeepEquals, []error{nil}) 380 381 ec2Client := ec2.StorageEC2(vs) 382 ec2Vols, err := ec2Client.Volumes(nil, nil) 383 c.Assert(err, jc.ErrorIsNil) 384 c.Assert(ec2Vols.Volumes, gc.HasLen, 2) 385 sortBySize(ec2Vols.Volumes) 386 c.Assert(ec2Vols.Volumes[0].Size, gc.Equals, 20) 387 } 388 389 func (s *ebsVolumeSuite) TestDescribeVolumes(c *gc.C) { 390 vs := s.volumeSource(c, nil) 391 s.assertCreateVolumes(c, vs, "") 392 393 vols, err := vs.DescribeVolumes([]string{"vol-0", "vol-1"}) 394 c.Assert(err, jc.ErrorIsNil) 395 c.Assert(vols, jc.DeepEquals, []storage.DescribeVolumesResult{{ 396 VolumeInfo: &storage.VolumeInfo{ 397 Size: 10240, 398 VolumeId: "vol-0", 399 Persistent: true, 400 }, 401 }, { 402 VolumeInfo: &storage.VolumeInfo{ 403 Size: 20480, 404 VolumeId: "vol-1", 405 Persistent: true, 406 }, 407 }}) 408 } 409 410 func (s *ebsVolumeSuite) TestDescribeVolumesNotFound(c *gc.C) { 411 vs := s.volumeSource(c, nil) 412 vols, err := vs.DescribeVolumes([]string{"vol-42"}) 413 c.Assert(err, jc.ErrorIsNil) 414 c.Assert(vols, gc.HasLen, 1) 415 c.Assert(vols[0].Error, gc.ErrorMatches, "vol-42 not found") 416 } 417 418 func (s *ebsVolumeSuite) TestListVolumes(c *gc.C) { 419 vs := s.volumeSource(c, nil) 420 s.assertCreateVolumes(c, vs, "") 421 422 // Only one volume created by assertCreateVolumes has 423 // the model-uuid tag with the expected value. 424 volIds, err := vs.ListVolumes() 425 c.Assert(err, jc.ErrorIsNil) 426 c.Assert(volIds, jc.SameContents, []string{"vol-0"}) 427 } 428 429 func (s *ebsVolumeSuite) TestCreateVolumesErrors(c *gc.C) { 430 vs := s.volumeSource(c, nil) 431 volume0 := names.NewVolumeTag("0") 432 433 instanceIdPending := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Pending, nil)[0] 434 instanceIdRunning := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Running, nil)[0] 435 attachmentParams := storage.VolumeAttachmentParams{ 436 AttachmentParams: storage.AttachmentParams{ 437 InstanceId: instance.Id(instanceIdRunning), 438 }, 439 } 440 441 for _, test := range []struct { 442 params storage.VolumeParams 443 err string 444 }{{ 445 params: storage.VolumeParams{ 446 Size: 1024, 447 Provider: ec2.EBS_ProviderType, 448 Attachment: &storage.VolumeAttachmentParams{ 449 AttachmentParams: storage.AttachmentParams{ 450 InstanceId: "woat", 451 }, 452 }, 453 }, 454 err: `querying instance details: instance "woat" not found \(InvalidInstanceID.NotFound\)`, 455 }, { 456 params: storage.VolumeParams{ 457 Size: 1024, 458 Provider: ec2.EBS_ProviderType, 459 Attachment: &storage.VolumeAttachmentParams{ 460 AttachmentParams: storage.AttachmentParams{ 461 InstanceId: instance.Id(instanceIdPending), 462 }, 463 }, 464 }, 465 err: "cannot attach to non-running instance i-3", 466 }, { 467 params: storage.VolumeParams{ 468 Size: 100000000, 469 Provider: ec2.EBS_ProviderType, 470 Attributes: map[string]interface{}{}, 471 Attachment: &attachmentParams, 472 }, 473 err: "volume size 97657 GiB exceeds the maximum of 1024 GiB", 474 }, { 475 params: storage.VolumeParams{ 476 Size: 100000000, 477 Provider: ec2.EBS_ProviderType, 478 Attributes: map[string]interface{}{ 479 "volume-type": "gp2", 480 }, 481 Attachment: &attachmentParams, 482 }, 483 err: "volume size 97657 GiB exceeds the maximum of 16384 GiB", 484 }, { 485 params: storage.VolumeParams{ 486 Size: 100000000, 487 Provider: ec2.EBS_ProviderType, 488 Attributes: map[string]interface{}{ 489 "volume-type": "io1", 490 "iops": "30", 491 }, 492 Attachment: &attachmentParams, 493 }, 494 err: "volume size 97657 GiB exceeds the maximum of 16384 GiB", 495 }, { 496 params: storage.VolumeParams{ 497 Tag: volume0, 498 Size: 1000, 499 Provider: ec2.EBS_ProviderType, 500 Attributes: map[string]interface{}{ 501 "volume-type": "io1", 502 "iops": "30", 503 }, 504 Attachment: &attachmentParams, 505 }, 506 err: "volume size is 1 GiB, must be at least 4 GiB", 507 }, { 508 params: storage.VolumeParams{ 509 Tag: volume0, 510 Size: 10000, 511 Provider: ec2.EBS_ProviderType, 512 Attributes: map[string]interface{}{ 513 "volume-type": "io1", 514 "iops": "1234", 515 }, 516 Attachment: &attachmentParams, 517 }, 518 err: "specified IOPS ratio is 1234/GiB, maximum is 30/GiB", 519 }, { 520 params: storage.VolumeParams{ 521 Tag: volume0, 522 Size: 10000, 523 Provider: ec2.EBS_ProviderType, 524 Attributes: map[string]interface{}{ 525 "volume-type": "standard", 526 "iops": "30", 527 }, 528 Attachment: &attachmentParams, 529 }, 530 err: `IOPS specified, but volume type is "standard"`, 531 }, { 532 params: storage.VolumeParams{ 533 Tag: volume0, 534 Size: 10000, 535 Provider: ec2.EBS_ProviderType, 536 Attributes: map[string]interface{}{ 537 "volume-type": "what", 538 }, 539 Attachment: &attachmentParams, 540 }, 541 err: "validating EBS storage config: volume-type: unexpected value \"what\"", 542 }} { 543 results, err := vs.CreateVolumes([]storage.VolumeParams{test.params}) 544 c.Assert(err, jc.ErrorIsNil) 545 c.Assert(results, gc.HasLen, 1) 546 c.Check(results[0].Error, gc.ErrorMatches, test.err) 547 } 548 } 549 550 var imageId = "ami-ccf405a5" // Ubuntu Maverick, i386, EBS store 551 552 func (s *ebsVolumeSuite) setupAttachVolumesTest( 553 c *gc.C, vs storage.VolumeSource, state awsec2.InstanceState, 554 ) []storage.VolumeAttachmentParams { 555 556 instanceId := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, state, nil)[0] 557 s.assertCreateVolumes(c, vs, instanceId) 558 559 return []storage.VolumeAttachmentParams{{ 560 Volume: names.NewVolumeTag("0"), 561 VolumeId: "vol-0", 562 AttachmentParams: storage.AttachmentParams{ 563 Machine: names.NewMachineTag("1"), 564 InstanceId: instance.Id(instanceId), 565 }, 566 }} 567 } 568 569 func (s *ebsVolumeSuite) TestAttachVolumesNotRunning(c *gc.C) { 570 vs := s.volumeSource(c, nil) 571 instanceId := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Pending, nil)[0] 572 results, err := s.createVolumes(vs, instanceId) 573 c.Assert(err, jc.ErrorIsNil) 574 c.Assert(results, gc.Not(gc.HasLen), 0) 575 for _, result := range results { 576 c.Check(errors.Cause(result.Error), gc.ErrorMatches, "cannot attach to non-running instance i-3") 577 } 578 } 579 580 func (s *ebsVolumeSuite) TestAttachVolumes(c *gc.C) { 581 vs := s.volumeSource(c, nil) 582 params := s.setupAttachVolumesTest(c, vs, ec2test.Running) 583 result, err := vs.AttachVolumes(params) 584 c.Assert(err, jc.ErrorIsNil) 585 c.Assert(result, gc.HasLen, 1) 586 c.Assert(result[0].Error, jc.ErrorIsNil) 587 c.Assert(result[0].VolumeAttachment, jc.DeepEquals, &storage.VolumeAttachment{ 588 names.NewVolumeTag("0"), 589 names.NewMachineTag("1"), 590 storage.VolumeAttachmentInfo{ 591 DeviceName: "xvdf", 592 ReadOnly: false, 593 }, 594 }) 595 596 ec2Client := ec2.StorageEC2(vs) 597 ec2Vols, err := ec2Client.Volumes(nil, nil) 598 c.Assert(err, jc.ErrorIsNil) 599 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 600 sortBySize(ec2Vols.Volumes) 601 c.Assert(ec2Vols.Volumes[0].Attachments, jc.DeepEquals, []awsec2.VolumeAttachment{{ 602 VolumeId: "vol-0", 603 InstanceId: "i-3", 604 Device: "/dev/sdf", 605 Status: "attached", 606 }}) 607 608 // Test idempotency. 609 result, err = vs.AttachVolumes(params) 610 c.Assert(err, jc.ErrorIsNil) 611 c.Assert(result, gc.HasLen, 1) 612 c.Assert(result[0].Error, jc.ErrorIsNil) 613 c.Assert(result[0].VolumeAttachment, jc.DeepEquals, &storage.VolumeAttachment{ 614 names.NewVolumeTag("0"), 615 names.NewMachineTag("1"), 616 storage.VolumeAttachmentInfo{ 617 DeviceName: "xvdf", 618 ReadOnly: false, 619 }, 620 }) 621 } 622 623 // TODO(axw) add tests for attempting to attach while 624 // a volume is still in the "creating" state. 625 626 func (s *ebsVolumeSuite) TestDetachVolumes(c *gc.C) { 627 vs := s.volumeSource(c, nil) 628 params := s.setupAttachVolumesTest(c, vs, ec2test.Running) 629 _, err := vs.AttachVolumes(params) 630 c.Assert(err, jc.ErrorIsNil) 631 errs, err := vs.DetachVolumes(params) 632 c.Assert(err, jc.ErrorIsNil) 633 c.Assert(errs, jc.DeepEquals, []error{nil}) 634 635 ec2Client := ec2.StorageEC2(vs) 636 ec2Vols, err := ec2Client.Volumes(nil, nil) 637 c.Assert(err, jc.ErrorIsNil) 638 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 639 sortBySize(ec2Vols.Volumes) 640 c.Assert(ec2Vols.Volumes[0].Attachments, gc.HasLen, 0) 641 642 // Test idempotent 643 errs, err = vs.DetachVolumes(params) 644 c.Assert(err, jc.ErrorIsNil) 645 c.Assert(errs, jc.DeepEquals, []error{nil}) 646 } 647 648 type blockDeviceMappingSuite struct { 649 testing.BaseSuite 650 } 651 652 var _ = gc.Suite(&blockDeviceMappingSuite{}) 653 654 func (*blockDeviceMappingSuite) TestBlockDeviceNamer(c *gc.C) { 655 var nextName func() (string, string, error) 656 expect := func(expectRequest, expectActual string) { 657 request, actual, err := nextName() 658 c.Assert(err, jc.ErrorIsNil) 659 c.Assert(request, gc.Equals, expectRequest) 660 c.Assert(actual, gc.Equals, expectActual) 661 } 662 expectN := func(expectRequest, expectActual string) { 663 for i := 1; i <= 6; i++ { 664 request, actual, err := nextName() 665 c.Assert(err, jc.ErrorIsNil) 666 c.Assert(request, gc.Equals, expectRequest+strconv.Itoa(i)) 667 c.Assert(actual, gc.Equals, expectActual+strconv.Itoa(i)) 668 } 669 } 670 expectErr := func(expectErr string) { 671 _, _, err := nextName() 672 c.Assert(err, gc.ErrorMatches, expectErr) 673 } 674 675 // First without numbers. 676 nextName = ec2.BlockDeviceNamer(false) 677 expect("/dev/sdf", "xvdf") 678 expect("/dev/sdg", "xvdg") 679 expect("/dev/sdh", "xvdh") 680 expect("/dev/sdi", "xvdi") 681 expect("/dev/sdj", "xvdj") 682 expect("/dev/sdk", "xvdk") 683 expect("/dev/sdl", "xvdl") 684 expect("/dev/sdm", "xvdm") 685 expect("/dev/sdn", "xvdn") 686 expect("/dev/sdo", "xvdo") 687 expect("/dev/sdp", "xvdp") 688 expectErr("too many EBS volumes to attach") 689 690 // Now with numbers. 691 nextName = ec2.BlockDeviceNamer(true) 692 expect("/dev/sdf1", "xvdf1") 693 expect("/dev/sdf2", "xvdf2") 694 expect("/dev/sdf3", "xvdf3") 695 expect("/dev/sdf4", "xvdf4") 696 expect("/dev/sdf5", "xvdf5") 697 expect("/dev/sdf6", "xvdf6") 698 expectN("/dev/sdg", "xvdg") 699 expectN("/dev/sdh", "xvdh") 700 expectN("/dev/sdi", "xvdi") 701 expectN("/dev/sdj", "xvdj") 702 expectN("/dev/sdk", "xvdk") 703 expectN("/dev/sdl", "xvdl") 704 expectN("/dev/sdm", "xvdm") 705 expectN("/dev/sdn", "xvdn") 706 expectN("/dev/sdo", "xvdo") 707 expectN("/dev/sdp", "xvdp") 708 expectErr("too many EBS volumes to attach") 709 } 710 711 func (*blockDeviceMappingSuite) TestGetBlockDeviceMappings(c *gc.C) { 712 mapping := ec2.GetBlockDeviceMappings(constraints.Value{}, "trusty") 713 c.Assert(mapping, gc.DeepEquals, []awsec2.BlockDeviceMapping{{ 714 VolumeSize: 8, 715 DeviceName: "/dev/sda1", 716 }, { 717 VirtualName: "ephemeral0", 718 DeviceName: "/dev/sdb", 719 }, { 720 VirtualName: "ephemeral1", 721 DeviceName: "/dev/sdc", 722 }, { 723 VirtualName: "ephemeral2", 724 DeviceName: "/dev/sdd", 725 }, { 726 VirtualName: "ephemeral3", 727 DeviceName: "/dev/sde", 728 }}) 729 }