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