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