github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/provider/azure/disks_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package azure
     5  
     6  import (
     7  	"encoding/xml"
     8  	"fmt"
     9  	"net/http"
    10  
    11  	"github.com/juju/names"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"launchpad.net/gwacl"
    15  
    16  	"github.com/juju/juju/storage"
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  var mediaLinkPrefix = fmt.Sprintf(
    21  	"https://account-name.blob.core.windows.net/vhds/%s/",
    22  	testing.EnvironmentTag.Id(),
    23  )
    24  
    25  type storageProviderSuite struct {
    26  	testing.BaseSuite
    27  }
    28  
    29  var _ = gc.Suite(&storageSuite{})
    30  
    31  func (*storageProviderSuite) TestValidateConfigUnknownConfig(c *gc.C) {
    32  	p := azureStorageProvider{}
    33  	cfg, err := storage.NewConfig("foo", storageProviderType, map[string]interface{}{
    34  		"unknown": "config",
    35  	})
    36  	c.Assert(err, jc.ErrorIsNil)
    37  	err = p.ValidateConfig(cfg)
    38  	c.Assert(err, jc.ErrorIsNil) // unknown attrs ignored
    39  }
    40  
    41  func (s *storageProviderSuite) TestSupports(c *gc.C) {
    42  	p := azureStorageProvider{}
    43  	c.Assert(p.Supports(storage.StorageKindBlock), jc.IsTrue)
    44  	c.Assert(p.Supports(storage.StorageKindFilesystem), jc.IsFalse)
    45  }
    46  
    47  var _ = gc.Suite(&azureVolumeSuite{})
    48  
    49  type azureVolumeSuite struct {
    50  	testing.BaseSuite
    51  }
    52  
    53  func (s *azureVolumeSuite) volumeSource(c *gc.C, cfg *storage.Config) storage.VolumeSource {
    54  	envCfg := makeEnviron(c).Config()
    55  	p := azureStorageProvider{}
    56  	vs, err := p.VolumeSource(envCfg, cfg)
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	return vs
    59  }
    60  
    61  func (s *azureVolumeSuite) TestCreateVolumes(c *gc.C) {
    62  	vs := s.volumeSource(c, nil)
    63  
    64  	machine := names.NewMachineTag("123")
    65  	volume0 := names.NewVolumeTag("0")
    66  	volume1 := names.NewVolumeTag("1")
    67  
    68  	env := makeEnviron(c)
    69  	prefix := env.getEnvPrefix()
    70  	serviceName := "service"
    71  	service := makeDeployment(env, prefix+serviceName)
    72  
    73  	roleName := service.Deployments[0].RoleList[0].RoleName
    74  	inst, err := env.getInstance(service, roleName)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  
    77  	params := []storage.VolumeParams{{
    78  		Tag:      volume0,
    79  		Size:     10 * 1000,
    80  		Provider: storageProviderType,
    81  		Attachment: &storage.VolumeAttachmentParams{
    82  			AttachmentParams: storage.AttachmentParams{
    83  				Machine:    machine,
    84  				InstanceId: inst.Id(),
    85  			},
    86  		},
    87  	}, {
    88  		Tag:      volume1,
    89  		Size:     20 * 1000,
    90  		Provider: storageProviderType,
    91  		Attachment: &storage.VolumeAttachmentParams{
    92  			AttachmentParams: storage.AttachmentParams{
    93  				Machine:    machine,
    94  				InstanceId: inst.Id(),
    95  			},
    96  		},
    97  	}}
    98  
    99  	getRoleResponse0, err := xml.Marshal(&gwacl.PersistentVMRole{})
   100  	c.Assert(err, jc.ErrorIsNil)
   101  
   102  	// Second time, respond saying LUN 0 is in use; this should
   103  	// cause LUN 1 to be assigned.
   104  	dataVirtualHardDisks := []gwacl.DataVirtualHardDisk{
   105  		{LUN: 0},
   106  	}
   107  	getRoleResponse1, err := xml.Marshal(&gwacl.PersistentVMRole{
   108  		DataVirtualHardDisks: &dataVirtualHardDisks,
   109  	})
   110  	c.Assert(err, jc.ErrorIsNil)
   111  
   112  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   113  		gwacl.NewDispatcherResponse(getRoleResponse0, http.StatusOK, nil),
   114  		gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), // AddDataDisk
   115  		gwacl.NewDispatcherResponse(getRoleResponse1, http.StatusOK, nil),
   116  		gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), // AddDataDisk
   117  	})
   118  
   119  	results, err := vs.CreateVolumes(params)
   120  	c.Assert(err, jc.ErrorIsNil)
   121  
   122  	c.Assert(results, gc.HasLen, 2)
   123  	c.Assert(results[0], jc.DeepEquals, storage.CreateVolumesResult{
   124  		Volume: &storage.Volume{
   125  			Tag: volume0,
   126  			VolumeInfo: storage.VolumeInfo{
   127  				VolumeId: "volume-0.vhd",
   128  				Size:     10 * 1024, // rounded up
   129  			},
   130  		},
   131  		VolumeAttachment: &storage.VolumeAttachment{
   132  			Volume:  volume0,
   133  			Machine: machine,
   134  			VolumeAttachmentInfo: storage.VolumeAttachmentInfo{
   135  				BusAddress: "scsi@5:0.0.0",
   136  			},
   137  		}},
   138  	)
   139  	c.Assert(results[1], jc.DeepEquals, storage.CreateVolumesResult{
   140  		Volume: &storage.Volume{
   141  			Tag: volume1,
   142  			VolumeInfo: storage.VolumeInfo{
   143  				VolumeId: "volume-1.vhd",
   144  				Size:     20 * 1024, // rounded up
   145  			},
   146  		},
   147  		VolumeAttachment: &storage.VolumeAttachment{
   148  			Volume:  volume1,
   149  			Machine: machine,
   150  			VolumeAttachmentInfo: storage.VolumeAttachmentInfo{
   151  				BusAddress: "scsi@5:0.0.1",
   152  			},
   153  		}},
   154  	)
   155  }
   156  
   157  func (s *azureVolumeSuite) TestCreateVolumesInvalidVolumeParams(c *gc.C) {
   158  	vs := s.volumeSource(c, nil)
   159  
   160  	args := storage.VolumeParams{
   161  		Tag:      names.NewVolumeTag("0"),
   162  		Size:     1023 * 1024,
   163  		Provider: storageProviderType,
   164  	}
   165  	err := vs.ValidateVolumeParams(args)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  
   168  	args.Size++ // One more MiB and we're out
   169  
   170  	results, err := vs.CreateVolumes([]storage.VolumeParams{args})
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	c.Assert(results[0].Error, gc.ErrorMatches, "1024 GiB exceeds the maximum of 1023 GiB")
   173  }
   174  
   175  func (s *azureVolumeSuite) TestCreateVolumesNoLuns(c *gc.C) {
   176  	vs := s.volumeSource(c, nil)
   177  
   178  	machine := names.NewMachineTag("123")
   179  	volume := names.NewVolumeTag("0")
   180  
   181  	env := makeEnviron(c)
   182  	prefix := env.getEnvPrefix()
   183  	serviceName := "service"
   184  	service := makeDeployment(env, prefix+serviceName)
   185  	roleName := service.Deployments[0].RoleList[0].RoleName
   186  	inst, err := env.getInstance(service, roleName)
   187  	c.Assert(err, jc.ErrorIsNil)
   188  
   189  	params := []storage.VolumeParams{{
   190  		Tag:      volume,
   191  		Size:     10 * 1000,
   192  		Provider: storageProviderType,
   193  		Attachment: &storage.VolumeAttachmentParams{
   194  			AttachmentParams: storage.AttachmentParams{
   195  				Machine:    machine,
   196  				InstanceId: inst.Id(),
   197  			},
   198  		},
   199  	}}
   200  
   201  	dataVirtualHardDisks := make([]gwacl.DataVirtualHardDisk, 32)
   202  	for i := range dataVirtualHardDisks {
   203  		dataVirtualHardDisks[i].LUN = i
   204  	}
   205  	getRoleResponse, err := xml.Marshal(&gwacl.PersistentVMRole{
   206  		DataVirtualHardDisks: &dataVirtualHardDisks,
   207  	})
   208  	c.Assert(err, jc.ErrorIsNil)
   209  
   210  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   211  		gwacl.NewDispatcherResponse(getRoleResponse, http.StatusOK, nil),
   212  	})
   213  
   214  	results, err := vs.CreateVolumes(params)
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	c.Assert(results, gc.HasLen, 1)
   217  	c.Assert(results[0].Error, gc.ErrorMatches, "choosing LUN: all LUNs are in use")
   218  }
   219  
   220  func (s *azureVolumeSuite) TestCreateVolumesLegacyInstance(c *gc.C) {
   221  	vs := s.volumeSource(c, nil)
   222  
   223  	machine := names.NewMachineTag("123")
   224  	volume := names.NewVolumeTag("0")
   225  
   226  	env := makeEnviron(c)
   227  	prefix := env.getEnvPrefix()
   228  	serviceName := "service"
   229  	service := makeLegacyDeployment(env, prefix+serviceName)
   230  	inst, err := env.getInstance(service, "")
   231  	c.Assert(err, jc.ErrorIsNil)
   232  
   233  	params := []storage.VolumeParams{{
   234  		Tag:      volume,
   235  		Size:     10 * 1000,
   236  		Provider: storageProviderType,
   237  		Attachment: &storage.VolumeAttachmentParams{
   238  			AttachmentParams: storage.AttachmentParams{
   239  				Machine:    machine,
   240  				InstanceId: inst.Id(),
   241  			},
   242  		},
   243  	}}
   244  
   245  	results, err := vs.CreateVolumes(params)
   246  	c.Assert(err, jc.ErrorIsNil)
   247  
   248  	c.Assert(results, gc.HasLen, 1)
   249  	c.Assert(results[0].Error, gc.ErrorMatches, "attaching disks to legacy instances not supported")
   250  }
   251  
   252  func (s *azureVolumeSuite) TestDestroyVolumes(c *gc.C) {
   253  	vs := s.volumeSource(c, nil)
   254  	results, err := vs.DestroyVolumes([]string{"volume-0.vhd", "volume-1.vhd"})
   255  	c.Assert(err, gc.ErrorMatches, "DestroyVolumes not supported")
   256  	c.Assert(results, gc.HasLen, 0)
   257  }
   258  
   259  func (s *azureVolumeSuite) TestDescribeVolumes(c *gc.C) {
   260  	vs := s.volumeSource(c, nil)
   261  
   262  	type disks struct {
   263  		Disks []gwacl.Disk `xml:"Disk"`
   264  	}
   265  
   266  	listDisksResponse, err := xml.Marshal(&disks{Disks: []gwacl.Disk{{
   267  		MediaLink:       mediaLinkPrefix + "volume-1.vhd",
   268  		LogicalSizeInGB: 22,
   269  	}, {
   270  		MediaLink:       mediaLinkPrefix + "volume-0.vhd",
   271  		LogicalSizeInGB: 11,
   272  	}, {
   273  		MediaLink:       "someOtherJunk.vhd",
   274  		LogicalSizeInGB: 33,
   275  	}}})
   276  	c.Assert(err, jc.ErrorIsNil)
   277  
   278  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   279  		gwacl.NewDispatcherResponse(listDisksResponse, http.StatusOK, nil),
   280  	})
   281  
   282  	volumes, err := vs.DescribeVolumes([]string{"volume-0.vhd", "volume-1.vhd"})
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	c.Assert(volumes, gc.HasLen, 2)
   285  	c.Assert(volumes, jc.DeepEquals, []storage.DescribeVolumesResult{{
   286  		VolumeInfo: &storage.VolumeInfo{
   287  			Size:     11 * 1024,
   288  			VolumeId: "volume-0.vhd",
   289  		},
   290  	}, {
   291  		VolumeInfo: &storage.VolumeInfo{
   292  			Size:     22 * 1024,
   293  			VolumeId: "volume-1.vhd",
   294  		},
   295  	}})
   296  }
   297  
   298  func (s *azureVolumeSuite) TestDescribeVolumesNotFound(c *gc.C) {
   299  	vs := s.volumeSource(c, nil)
   300  
   301  	type disks struct {
   302  		Disks []gwacl.Disk `xml:"Disk"`
   303  	}
   304  
   305  	listDisksResponse, err := xml.Marshal(&disks{Disks: []gwacl.Disk{{
   306  		MediaLink:       mediaLinkPrefix + "volume-0.vhd",
   307  		LogicalSizeInGB: 11,
   308  	}}})
   309  	c.Assert(err, jc.ErrorIsNil)
   310  
   311  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   312  		gwacl.NewDispatcherResponse(listDisksResponse, http.StatusOK, nil),
   313  	})
   314  
   315  	volumes, err := vs.DescribeVolumes([]string{"volume-0.vhd", "volume-1.vhd"})
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(volumes, gc.HasLen, 2)
   318  	c.Assert(volumes[0].Error, gc.IsNil)
   319  	c.Assert(volumes[1].Error, gc.ErrorMatches, "volume volume-1.vhd not found")
   320  }
   321  
   322  func (s *azureVolumeSuite) TestListVolumes(c *gc.C) {
   323  	vs := s.volumeSource(c, nil)
   324  
   325  	type disks struct {
   326  		Disks []gwacl.Disk `xml:"Disk"`
   327  	}
   328  
   329  	listDisksResponse, err := xml.Marshal(&disks{Disks: []gwacl.Disk{{
   330  		MediaLink:       mediaLinkPrefix + "volume-1.vhd",
   331  		LogicalSizeInGB: 22,
   332  	}, {
   333  		MediaLink:       mediaLinkPrefix + "volume-0.vhd",
   334  		LogicalSizeInGB: 11,
   335  	}, {
   336  		MediaLink:       "someOtherJunk.vhd",
   337  		LogicalSizeInGB: 33,
   338  	}}})
   339  	c.Assert(err, jc.ErrorIsNil)
   340  
   341  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   342  		gwacl.NewDispatcherResponse(listDisksResponse, http.StatusOK, nil),
   343  	})
   344  
   345  	volIds, err := vs.ListVolumes()
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	c.Assert(volIds, jc.SameContents, []string{"volume-0.vhd", "volume-1.vhd"})
   348  }
   349  
   350  func (s *azureVolumeSuite) TestAttachVolumesAlreadyAttached(c *gc.C) {
   351  	vs := s.volumeSource(c, nil)
   352  
   353  	machine0 := names.NewMachineTag("0")
   354  	machine1 := names.NewMachineTag("1")
   355  	volume0 := names.NewVolumeTag("0")
   356  	volume1 := names.NewVolumeTag("1")
   357  	volume2 := names.NewVolumeTag("2")
   358  
   359  	env := makeEnviron(c)
   360  	prefix := env.getEnvPrefix()
   361  	service := makeDeployment(env, prefix+"service")
   362  	roleName0 := service.Deployments[0].RoleList[0].RoleName
   363  	roleName1 := service.Deployments[0].RoleList[1].RoleName
   364  	inst0, err := env.getInstance(service, roleName0)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	inst1, err := env.getInstance(service, roleName1)
   367  	c.Assert(err, jc.ErrorIsNil)
   368  
   369  	// First VM.
   370  	dataVirtualHardDisks0 := []gwacl.DataVirtualHardDisk{{
   371  		MediaLink:           mediaLinkPrefix + "volume-0.vhd",
   372  		LUN:                 0,
   373  		LogicalDiskSizeInGB: 1,
   374  	}, {
   375  		MediaLink:           mediaLinkPrefix + "volume-1.vhd",
   376  		LUN:                 1,
   377  		LogicalDiskSizeInGB: 2,
   378  	}}
   379  	getRoleResponse0, err := xml.Marshal(&gwacl.PersistentVMRole{
   380  		DataVirtualHardDisks: &dataVirtualHardDisks0,
   381  	})
   382  	c.Assert(err, jc.ErrorIsNil)
   383  
   384  	// Second VM.
   385  	dataVirtualHardDisks1 := []gwacl.DataVirtualHardDisk{{
   386  		MediaLink:           mediaLinkPrefix + "volume-2.vhd",
   387  		LUN:                 0,
   388  		LogicalDiskSizeInGB: 3,
   389  	}}
   390  	getRoleResponse1, err := xml.Marshal(&gwacl.PersistentVMRole{
   391  		DataVirtualHardDisks: &dataVirtualHardDisks1,
   392  	})
   393  	c.Assert(err, jc.ErrorIsNil)
   394  
   395  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   396  		gwacl.NewDispatcherResponse(getRoleResponse0, http.StatusOK, nil),
   397  		gwacl.NewDispatcherResponse(getRoleResponse1, http.StatusOK, nil),
   398  	})
   399  
   400  	results, err := vs.AttachVolumes([]storage.VolumeAttachmentParams{{
   401  		Volume:   volume0,
   402  		VolumeId: "volume-0.vhd",
   403  		AttachmentParams: storage.AttachmentParams{
   404  			Machine:    machine0,
   405  			InstanceId: inst0.Id(),
   406  		},
   407  	}, {
   408  		Volume:   volume1,
   409  		VolumeId: "volume-1.vhd",
   410  		AttachmentParams: storage.AttachmentParams{
   411  			Machine:    machine0,
   412  			InstanceId: inst0.Id(),
   413  		},
   414  	}, {
   415  		Volume:   volume2,
   416  		VolumeId: "volume-2.vhd",
   417  		AttachmentParams: storage.AttachmentParams{
   418  			Machine:    machine1,
   419  			InstanceId: inst1.Id(),
   420  		},
   421  	}})
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	c.Assert(results, jc.DeepEquals, []storage.AttachVolumesResult{{
   424  		VolumeAttachment: &storage.VolumeAttachment{
   425  			Volume:  volume0,
   426  			Machine: machine0,
   427  			VolumeAttachmentInfo: storage.VolumeAttachmentInfo{
   428  				BusAddress: "scsi@5:0.0.0",
   429  			},
   430  		},
   431  	}, {
   432  		VolumeAttachment: &storage.VolumeAttachment{
   433  			Volume:  volume1,
   434  			Machine: machine0,
   435  			VolumeAttachmentInfo: storage.VolumeAttachmentInfo{
   436  				BusAddress: "scsi@5:0.0.1",
   437  			},
   438  		},
   439  	}, {
   440  		VolumeAttachment: &storage.VolumeAttachment{
   441  			Volume:  volume2,
   442  			Machine: machine1,
   443  			VolumeAttachmentInfo: storage.VolumeAttachmentInfo{
   444  				BusAddress: "scsi@5:0.0.0",
   445  			},
   446  		},
   447  	}})
   448  }
   449  
   450  func (s *azureVolumeSuite) TestAttachVolumesNotAttached(c *gc.C) {
   451  	vs := s.volumeSource(c, nil)
   452  
   453  	machine := names.NewMachineTag("0")
   454  	volume := names.NewVolumeTag("0")
   455  
   456  	env := makeEnviron(c)
   457  	prefix := env.getEnvPrefix()
   458  	service := makeDeployment(env, prefix+"service")
   459  	roleName := service.Deployments[0].RoleList[0].RoleName
   460  	inst, err := env.getInstance(service, roleName)
   461  	c.Assert(err, jc.ErrorIsNil)
   462  
   463  	getRoleResponse, err := xml.Marshal(&gwacl.PersistentVMRole{})
   464  	c.Assert(err, jc.ErrorIsNil)
   465  
   466  	gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{
   467  		gwacl.NewDispatcherResponse(getRoleResponse, http.StatusOK, nil),
   468  	})
   469  
   470  	results, err := vs.AttachVolumes([]storage.VolumeAttachmentParams{{
   471  		Volume:   volume,
   472  		VolumeId: "volume-0.vhd",
   473  		AttachmentParams: storage.AttachmentParams{
   474  			Machine:    machine,
   475  			InstanceId: inst.Id(),
   476  		},
   477  	}})
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	c.Assert(results, gc.HasLen, 1)
   480  	c.Assert(results[0].Error, gc.ErrorMatches, "attaching volumes not supported")
   481  }
   482  
   483  func (s *azureVolumeSuite) TestAttachVolumesGetRoleError(c *gc.C) {
   484  	vs := s.volumeSource(c, nil)
   485  
   486  	machine := names.NewMachineTag("0")
   487  	volume := names.NewVolumeTag("0")
   488  
   489  	env := makeEnviron(c)
   490  	prefix := env.getEnvPrefix()
   491  	service := makeLegacyDeployment(env, prefix+"service")
   492  	inst, err := env.getInstance(service, "")
   493  	c.Assert(err, jc.ErrorIsNil)
   494  
   495  	results, err := vs.AttachVolumes([]storage.VolumeAttachmentParams{{
   496  		Volume:   volume,
   497  		VolumeId: "volume-0.vhd",
   498  		AttachmentParams: storage.AttachmentParams{
   499  			Machine:    machine,
   500  			InstanceId: inst.Id(),
   501  		},
   502  	}})
   503  	c.Assert(err, jc.ErrorIsNil)
   504  	c.Assert(results, gc.HasLen, 1)
   505  	c.Assert(results[0].Error, gc.ErrorMatches, "attaching disks to legacy instances not supported")
   506  }
   507  
   508  func (s *azureVolumeSuite) TestDetachVolumes(c *gc.C) {
   509  	vs := s.volumeSource(c, nil)
   510  	_, err := vs.DetachVolumes([]storage.VolumeAttachmentParams{{}})
   511  	c.Assert(err, gc.ErrorMatches, "detaching volumes not supported")
   512  }