github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 jc "github.com/juju/testing/checkers" 14 awsec2 "gopkg.in/amz.v3/ec2" 15 "gopkg.in/amz.v3/ec2/ec2test" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/constraints" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/environs/jujutest" 21 "github.com/juju/juju/instance" 22 "github.com/juju/juju/juju/arch" 23 "github.com/juju/juju/provider/ec2" 24 "github.com/juju/juju/storage" 25 "github.com/juju/juju/testing" 26 "github.com/juju/juju/version" 27 ) 28 29 type storageSuite struct { 30 testing.BaseSuite 31 } 32 33 var _ = gc.Suite(&storageSuite{}) 34 35 func (*storageSuite) TestValidateConfigUnknownConfig(c *gc.C) { 36 p := ec2.EBSProvider() 37 cfg, err := storage.NewConfig("foo", ec2.EBS_ProviderType, map[string]interface{}{ 38 "unknown": "config", 39 }) 40 c.Assert(err, jc.ErrorIsNil) 41 err = p.ValidateConfig(cfg) 42 c.Assert(err, jc.ErrorIsNil) // unknown attrs ignored 43 } 44 45 func (s *storageSuite) TestSupports(c *gc.C) { 46 p := ec2.EBSProvider() 47 c.Assert(p.Supports(storage.StorageKindBlock), jc.IsTrue) 48 c.Assert(p.Supports(storage.StorageKindFilesystem), jc.IsFalse) 49 } 50 51 var _ = gc.Suite(&ebsVolumeSuite{}) 52 53 type ebsVolumeSuite struct { 54 testing.BaseSuite 55 jujutest.Tests 56 srv localServer 57 restoreEC2Patching func() 58 59 instanceId string 60 } 61 62 func (s *ebsVolumeSuite) SetUpSuite(c *gc.C) { 63 // Upload arches that ec2 supports; add to this 64 // as ec2 coverage expands. 65 s.UploadArches = []string{arch.AMD64, arch.I386} 66 s.TestConfig = localConfigAttrs 67 s.restoreEC2Patching = patchEC2ForTesting() 68 s.BaseSuite.SetUpSuite(c) 69 } 70 71 func (s *ebsVolumeSuite) TearDownSuite(c *gc.C) { 72 s.BaseSuite.TearDownSuite(c) 73 s.restoreEC2Patching() 74 } 75 76 func (s *ebsVolumeSuite) SetUpTest(c *gc.C) { 77 s.BaseSuite.SetUpTest(c) 78 s.srv.startServer(c) 79 s.Tests.SetUpTest(c) 80 s.PatchValue(&version.Current, version.Binary{ 81 Number: version.Current.Number, 82 Series: testing.FakeDefaultSeries, 83 Arch: arch.AMD64, 84 }) 85 } 86 87 func (s *ebsVolumeSuite) TearDownTest(c *gc.C) { 88 s.Tests.TearDownTest(c) 89 s.srv.stopServer(c) 90 s.BaseSuite.TearDownTest(c) 91 } 92 93 func (s *ebsVolumeSuite) volumeSource(c *gc.C, cfg *storage.Config) storage.VolumeSource { 94 envCfg, err := config.New(config.NoDefaults, s.TestConfig) 95 c.Assert(err, jc.ErrorIsNil) 96 p := ec2.EBSProvider() 97 vs, err := p.VolumeSource(envCfg, cfg) 98 c.Assert(err, jc.ErrorIsNil) 99 return vs 100 } 101 102 func (s *ebsVolumeSuite) createVolumes(vs storage.VolumeSource, instanceId string) ([]storage.Volume, error) { 103 if instanceId == "" { 104 instanceId = s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Running, nil)[0] 105 } 106 volume0 := names.NewVolumeTag("0") 107 volume1 := names.NewVolumeTag("1") 108 volume2 := names.NewVolumeTag("2") 109 params := []storage.VolumeParams{{ 110 Tag: volume0, 111 Size: 10 * 1000, 112 Provider: ec2.EBS_ProviderType, 113 Attributes: map[string]interface{}{ 114 "persistent": true, 115 "volume-type": "io1", 116 "iops": 100, 117 }, 118 Attachment: &storage.VolumeAttachmentParams{ 119 AttachmentParams: storage.AttachmentParams{ 120 InstanceId: instance.Id(instanceId), 121 }, 122 }, 123 }, { 124 Tag: volume1, 125 Size: 20 * 1000, 126 Provider: ec2.EBS_ProviderType, 127 Attributes: map[string]interface{}{ 128 "persistent": true, 129 }, 130 Attachment: &storage.VolumeAttachmentParams{ 131 AttachmentParams: storage.AttachmentParams{ 132 InstanceId: instance.Id(instanceId), 133 }, 134 }, 135 }, { 136 Tag: volume2, 137 Size: 30 * 1000, 138 Provider: ec2.EBS_ProviderType, 139 ResourceTags: map[string]string{ 140 "abc": "123", 141 }, 142 Attachment: &storage.VolumeAttachmentParams{ 143 AttachmentParams: storage.AttachmentParams{ 144 InstanceId: instance.Id(instanceId), 145 }, 146 }, 147 }} 148 vols, _, err := vs.CreateVolumes(params) 149 return vols, err 150 } 151 152 func (s *ebsVolumeSuite) assertCreateVolumes(c *gc.C, vs storage.VolumeSource, instanceId string) { 153 vols, err := s.createVolumes(vs, instanceId) 154 c.Assert(err, jc.ErrorIsNil) 155 c.Assert(vols, gc.HasLen, 3) 156 c.Assert(vols, jc.SameContents, []storage.Volume{{ 157 names.NewVolumeTag("0"), 158 storage.VolumeInfo{ 159 Size: 10240, 160 VolumeId: "vol-0", 161 Persistent: true, 162 }, 163 }, { 164 names.NewVolumeTag("1"), 165 storage.VolumeInfo{ 166 Size: 20480, 167 VolumeId: "vol-1", 168 Persistent: true, 169 }, 170 }, { 171 names.NewVolumeTag("2"), 172 storage.VolumeInfo{ 173 Size: 30720, 174 VolumeId: "vol-2", 175 Persistent: false, 176 }, 177 }}) 178 ec2Client := ec2.StorageEC2(vs) 179 ec2Vols, err := ec2Client.Volumes(nil, nil) 180 c.Assert(err, jc.ErrorIsNil) 181 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 182 sortBySize(ec2Vols.Volumes) 183 c.Assert(ec2Vols.Volumes[0].Size, gc.Equals, 10) 184 c.Assert(ec2Vols.Volumes[1].Size, gc.Equals, 20) 185 c.Assert(ec2Vols.Volumes[2].Size, gc.Equals, 30) 186 } 187 188 type volumeSorter struct { 189 vols []awsec2.Volume 190 less func(i, j awsec2.Volume) bool 191 } 192 193 func sortBySize(vols []awsec2.Volume) { 194 sort.Sort(volumeSorter{vols, func(i, j awsec2.Volume) bool { 195 return i.Size < j.Size 196 }}) 197 } 198 199 func (s volumeSorter) Len() int { 200 return len(s.vols) 201 } 202 203 func (s volumeSorter) Swap(i, j int) { 204 s.vols[i], s.vols[j] = s.vols[j], s.vols[i] 205 } 206 207 func (s volumeSorter) Less(i, j int) bool { 208 return s.less(s.vols[i], s.vols[j]) 209 } 210 211 func (s *ebsVolumeSuite) TestCreateVolumes(c *gc.C) { 212 vs := s.volumeSource(c, nil) 213 s.assertCreateVolumes(c, vs, "") 214 } 215 216 func (s *ebsVolumeSuite) TestVolumeTags(c *gc.C) { 217 vs := s.volumeSource(c, nil) 218 vols, err := s.createVolumes(vs, "") 219 c.Assert(err, jc.ErrorIsNil) 220 c.Assert(vols, gc.HasLen, 3) 221 c.Assert(vols, jc.SameContents, []storage.Volume{{ 222 names.NewVolumeTag("0"), 223 storage.VolumeInfo{ 224 Size: 10240, 225 VolumeId: "vol-0", 226 Persistent: true, 227 }, 228 }, { 229 names.NewVolumeTag("1"), 230 storage.VolumeInfo{ 231 Size: 20480, 232 VolumeId: "vol-1", 233 Persistent: true, 234 }, 235 }, { 236 names.NewVolumeTag("2"), 237 storage.VolumeInfo{ 238 Size: 30720, 239 VolumeId: "vol-2", 240 Persistent: false, 241 }, 242 }}) 243 ec2Client := ec2.StorageEC2(vs) 244 ec2Vols, err := ec2Client.Volumes(nil, nil) 245 c.Assert(err, jc.ErrorIsNil) 246 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 247 sortBySize(ec2Vols.Volumes) 248 c.Assert(ec2Vols.Volumes[0].Tags, jc.SameContents, []awsec2.Tag{ 249 {"Name", "juju-sample-volume-0"}, 250 }) 251 c.Assert(ec2Vols.Volumes[1].Tags, jc.SameContents, []awsec2.Tag{ 252 {"Name", "juju-sample-volume-1"}, 253 }) 254 c.Assert(ec2Vols.Volumes[2].Tags, jc.SameContents, []awsec2.Tag{ 255 {"Name", "juju-sample-volume-2"}, 256 {"abc", "123"}, 257 }) 258 } 259 260 func (s *ebsVolumeSuite) TestVolumeTypeAliases(c *gc.C) { 261 instanceIdRunning := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Running, nil)[0] 262 vs := s.volumeSource(c, nil) 263 ec2Client := ec2.StorageEC2(vs) 264 aliases := [][2]string{ 265 {"magnetic", "standard"}, 266 {"ssd", "gp2"}, 267 {"provisioned-iops", "io1"}, 268 } 269 for i, alias := range aliases { 270 params := []storage.VolumeParams{{ 271 Tag: names.NewVolumeTag("0"), 272 Size: 10 * 1000, 273 Provider: ec2.EBS_ProviderType, 274 Attributes: map[string]interface{}{ 275 "volume-type": alias[0], 276 }, 277 Attachment: &storage.VolumeAttachmentParams{ 278 AttachmentParams: storage.AttachmentParams{ 279 InstanceId: instance.Id(instanceIdRunning), 280 }, 281 }, 282 }} 283 if alias[1] == "io1" { 284 params[0].Attributes["iops"] = 100 285 } 286 vols, _, err := vs.CreateVolumes(params) 287 c.Assert(err, jc.ErrorIsNil) 288 c.Assert(vols, gc.HasLen, 1) 289 c.Assert(vols[0].VolumeId, gc.Equals, fmt.Sprintf("vol-%d", i)) 290 } 291 ec2Vols, err := ec2Client.Volumes(nil, nil) 292 c.Assert(err, jc.ErrorIsNil) 293 c.Assert(ec2Vols.Volumes, gc.HasLen, len(aliases)) 294 sort.Sort(volumeSorter{ec2Vols.Volumes, func(i, j awsec2.Volume) bool { 295 return i.Id < j.Id 296 }}) 297 for i, alias := range aliases { 298 c.Assert(ec2Vols.Volumes[i].VolumeType, gc.Equals, alias[1]) 299 } 300 } 301 302 func (s *ebsVolumeSuite) TestDeleteVolumes(c *gc.C) { 303 vs := s.volumeSource(c, nil) 304 params := s.setupAttachVolumesTest(c, vs, ec2test.Running) 305 err := vs.DetachVolumes(params) 306 c.Assert(err, jc.ErrorIsNil) 307 errs := vs.DestroyVolumes([]string{"vol-0"}) 308 c.Assert(errs, jc.DeepEquals, []error{nil}) 309 310 ec2Client := ec2.StorageEC2(vs) 311 ec2Vols, err := ec2Client.Volumes(nil, nil) 312 c.Assert(err, jc.ErrorIsNil) 313 c.Assert(ec2Vols.Volumes, gc.HasLen, 2) 314 sortBySize(ec2Vols.Volumes) 315 c.Assert(ec2Vols.Volumes[0].Size, gc.Equals, 20) 316 } 317 318 func (s *ebsVolumeSuite) TestVolumes(c *gc.C) { 319 vs := s.volumeSource(c, nil) 320 s.assertCreateVolumes(c, vs, "") 321 322 vols, err := vs.DescribeVolumes([]string{"vol-0", "vol-1"}) 323 c.Assert(err, jc.ErrorIsNil) 324 c.Assert(vols, gc.HasLen, 2) 325 c.Assert(vols, jc.SameContents, []storage.VolumeInfo{{ 326 Size: 10240, 327 VolumeId: "vol-0", 328 }, { 329 Size: 20480, 330 VolumeId: "vol-1", 331 }}) 332 } 333 334 func (s *ebsVolumeSuite) TestCreateVolumesErrors(c *gc.C) { 335 vs := s.volumeSource(c, nil) 336 volume0 := names.NewVolumeTag("0") 337 338 instanceIdPending := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Pending, nil)[0] 339 instanceIdRunning := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Running, nil)[0] 340 attachmentParams := storage.VolumeAttachmentParams{ 341 AttachmentParams: storage.AttachmentParams{ 342 InstanceId: instance.Id(instanceIdRunning), 343 }, 344 } 345 346 for _, test := range []struct { 347 params storage.VolumeParams 348 err string 349 }{{ 350 params: storage.VolumeParams{ 351 Provider: ec2.EBS_ProviderType, 352 Attachment: &storage.VolumeAttachmentParams{ 353 AttachmentParams: storage.AttachmentParams{ 354 InstanceId: "woat", 355 }, 356 }, 357 }, 358 err: `querying instance details: instance "woat" not found \(InvalidInstanceID.NotFound\)`, 359 }, { 360 params: storage.VolumeParams{ 361 Provider: ec2.EBS_ProviderType, 362 Attachment: &storage.VolumeAttachmentParams{ 363 AttachmentParams: storage.AttachmentParams{ 364 InstanceId: instance.Id(instanceIdPending), 365 }, 366 }, 367 }, 368 err: "querying instance details: volumes can only be attached to running instances, these instances are not running: i-3", 369 }, { 370 params: storage.VolumeParams{ 371 Size: 100000000, 372 Provider: ec2.EBS_ProviderType, 373 Attributes: map[string]interface{}{}, 374 Attachment: &attachmentParams, 375 }, 376 err: "97657 GiB exceeds the maximum of 1024 GiB", 377 }, { 378 params: storage.VolumeParams{ 379 Tag: volume0, 380 Size: 1000, 381 Provider: ec2.EBS_ProviderType, 382 Attributes: map[string]interface{}{ 383 "volume-type": "io1", 384 "iops": "1234", 385 }, 386 Attachment: &attachmentParams, 387 }, 388 err: "volume size is 1 GiB, must be at least 10 GiB for provisioned IOPS", 389 }, { 390 params: storage.VolumeParams{ 391 Tag: volume0, 392 Size: 10000, 393 Provider: ec2.EBS_ProviderType, 394 Attributes: map[string]interface{}{ 395 "volume-type": "io1", 396 "iops": "1234", 397 }, 398 Attachment: &attachmentParams, 399 }, 400 err: "volume size is 10 GiB, must be at least 41 GiB to support 1234 IOPS", 401 }, { 402 params: storage.VolumeParams{ 403 Tag: volume0, 404 Size: 10000, 405 Provider: ec2.EBS_ProviderType, 406 Attributes: map[string]interface{}{ 407 "volume-type": "standard", 408 "iops": "1234", 409 }, 410 Attachment: &attachmentParams, 411 }, 412 err: `IOPS specified, but volume type is "standard"`, 413 }, { 414 params: storage.VolumeParams{ 415 Tag: volume0, 416 Size: 10000, 417 Provider: ec2.EBS_ProviderType, 418 Attributes: map[string]interface{}{ 419 "volume-type": "what", 420 }, 421 Attachment: &attachmentParams, 422 }, 423 err: "validating EBS storage config: volume-type: unexpected value \"what\"", 424 }} { 425 _, _, err := vs.CreateVolumes([]storage.VolumeParams{test.params}) 426 c.Check(err, gc.ErrorMatches, test.err) 427 } 428 } 429 430 var imageId = "ami-ccf405a5" // Ubuntu Maverick, i386, EBS store 431 432 func (s *ebsVolumeSuite) setupAttachVolumesTest( 433 c *gc.C, vs storage.VolumeSource, state awsec2.InstanceState, 434 ) []storage.VolumeAttachmentParams { 435 436 instanceId := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, state, nil)[0] 437 s.assertCreateVolumes(c, vs, instanceId) 438 439 return []storage.VolumeAttachmentParams{{ 440 Volume: names.NewVolumeTag("0"), 441 VolumeId: "vol-0", 442 AttachmentParams: storage.AttachmentParams{ 443 Machine: names.NewMachineTag("1"), 444 InstanceId: instance.Id(instanceId), 445 }, 446 }} 447 } 448 449 func (s *ebsVolumeSuite) TestAttachVolumesNotRunning(c *gc.C) { 450 vs := s.volumeSource(c, nil) 451 instanceId := s.srv.ec2srv.NewInstances(1, "m1.medium", imageId, ec2test.Pending, nil)[0] 452 _, err := s.createVolumes(vs, instanceId) 453 c.Assert(errors.Cause(err), gc.ErrorMatches, ".* these instances are not running: i-3") 454 } 455 456 func (s *ebsVolumeSuite) TestAttachVolumes(c *gc.C) { 457 vs := s.volumeSource(c, nil) 458 params := s.setupAttachVolumesTest(c, vs, ec2test.Running) 459 result, err := vs.AttachVolumes(params) 460 c.Assert(err, jc.ErrorIsNil) 461 c.Assert(result, gc.HasLen, 1) 462 c.Assert(result[0], gc.Equals, storage.VolumeAttachment{ 463 names.NewVolumeTag("0"), 464 names.NewMachineTag("1"), 465 storage.VolumeAttachmentInfo{ 466 DeviceName: "xvdf", 467 ReadOnly: false, 468 }, 469 }) 470 471 ec2Client := ec2.StorageEC2(vs) 472 ec2Vols, err := ec2Client.Volumes(nil, nil) 473 c.Assert(err, jc.ErrorIsNil) 474 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 475 sortBySize(ec2Vols.Volumes) 476 c.Assert(ec2Vols.Volumes[0].Attachments, jc.DeepEquals, []awsec2.VolumeAttachment{{ 477 VolumeId: "vol-0", 478 InstanceId: "i-3", 479 Device: "/dev/sdf", 480 Status: "attached", 481 DeleteOnTermination: true, 482 }}) 483 484 // Test idempotency. 485 result, err = vs.AttachVolumes(params) 486 c.Assert(err, jc.ErrorIsNil) 487 c.Assert(result, gc.HasLen, 1) 488 c.Assert(result[0], gc.Equals, storage.VolumeAttachment{ 489 names.NewVolumeTag("0"), 490 names.NewMachineTag("1"), 491 storage.VolumeAttachmentInfo{ 492 DeviceName: "xvdf", 493 ReadOnly: false, 494 }, 495 }) 496 } 497 498 func (s *ebsVolumeSuite) TestDetachVolumes(c *gc.C) { 499 vs := s.volumeSource(c, nil) 500 params := s.setupAttachVolumesTest(c, vs, ec2test.Running) 501 _, err := vs.AttachVolumes(params) 502 c.Assert(err, jc.ErrorIsNil) 503 err = vs.DetachVolumes(params) 504 c.Assert(err, jc.ErrorIsNil) 505 506 ec2Client := ec2.StorageEC2(vs) 507 ec2Vols, err := ec2Client.Volumes(nil, nil) 508 c.Assert(err, jc.ErrorIsNil) 509 c.Assert(ec2Vols.Volumes, gc.HasLen, 3) 510 sortBySize(ec2Vols.Volumes) 511 c.Assert(ec2Vols.Volumes[0].Attachments, gc.HasLen, 0) 512 513 // Test idempotent 514 err = vs.DetachVolumes(params) 515 c.Assert(err, jc.ErrorIsNil) 516 } 517 518 type blockDeviceMappingSuite struct { 519 testing.BaseSuite 520 } 521 522 var _ = gc.Suite(&blockDeviceMappingSuite{}) 523 524 func (*blockDeviceMappingSuite) TestBlockDeviceNamer(c *gc.C) { 525 var nextName func() (string, string, error) 526 expect := func(expectRequest, expectActual string) { 527 request, actual, err := nextName() 528 c.Assert(err, jc.ErrorIsNil) 529 c.Assert(request, gc.Equals, expectRequest) 530 c.Assert(actual, gc.Equals, expectActual) 531 } 532 expectN := func(expectRequest, expectActual string) { 533 for i := 1; i <= 6; i++ { 534 request, actual, err := nextName() 535 c.Assert(err, jc.ErrorIsNil) 536 c.Assert(request, gc.Equals, expectRequest+strconv.Itoa(i)) 537 c.Assert(actual, gc.Equals, expectActual+strconv.Itoa(i)) 538 } 539 } 540 expectErr := func(expectErr string) { 541 _, _, err := nextName() 542 c.Assert(err, gc.ErrorMatches, expectErr) 543 } 544 545 // First without numbers. 546 nextName = ec2.BlockDeviceNamer(awsec2.Instance{ 547 VirtType: "hvm", 548 }) 549 expect("/dev/sdf", "xvdf") 550 expect("/dev/sdg", "xvdg") 551 expect("/dev/sdh", "xvdh") 552 expect("/dev/sdi", "xvdi") 553 expect("/dev/sdj", "xvdj") 554 expect("/dev/sdk", "xvdk") 555 expect("/dev/sdl", "xvdl") 556 expect("/dev/sdm", "xvdm") 557 expect("/dev/sdn", "xvdn") 558 expect("/dev/sdo", "xvdo") 559 expect("/dev/sdp", "xvdp") 560 expectErr("too many EBS volumes to attach") 561 562 // Now with numbers. 563 nextName = ec2.BlockDeviceNamer(awsec2.Instance{ 564 VirtType: "paravirtual", 565 }) 566 expect("/dev/sdf1", "xvdf1") 567 expect("/dev/sdf2", "xvdf2") 568 expect("/dev/sdf3", "xvdf3") 569 expect("/dev/sdf4", "xvdf4") 570 expect("/dev/sdf5", "xvdf5") 571 expect("/dev/sdf6", "xvdf6") 572 expectN("/dev/sdg", "xvdg") 573 expectN("/dev/sdh", "xvdh") 574 expectN("/dev/sdi", "xvdi") 575 expectN("/dev/sdj", "xvdj") 576 expectN("/dev/sdk", "xvdk") 577 expectN("/dev/sdl", "xvdl") 578 expectN("/dev/sdm", "xvdm") 579 expectN("/dev/sdn", "xvdn") 580 expectN("/dev/sdo", "xvdo") 581 expectN("/dev/sdp", "xvdp") 582 expectErr("too many EBS volumes to attach") 583 } 584 585 func (*blockDeviceMappingSuite) TestGetBlockDeviceMappings(c *gc.C) { 586 mapping, err := ec2.GetBlockDeviceMappings(constraints.Value{}) 587 c.Assert(err, jc.ErrorIsNil) 588 c.Assert(mapping, gc.DeepEquals, []awsec2.BlockDeviceMapping{{ 589 VolumeSize: 8, 590 DeviceName: "/dev/sda1", 591 }, { 592 VirtualName: "ephemeral0", 593 DeviceName: "/dev/sdb", 594 }, { 595 VirtualName: "ephemeral1", 596 DeviceName: "/dev/sdc", 597 }, { 598 VirtualName: "ephemeral2", 599 DeviceName: "/dev/sdd", 600 }, { 601 VirtualName: "ephemeral3", 602 DeviceName: "/dev/sde", 603 }}) 604 }