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