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  }