github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/storage/provider/loop_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provider_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/storage"
    17  	"github.com/juju/juju/storage/provider"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  const stubMachineId = "machine101"
    22  
    23  var _ = gc.Suite(&loopSuite{})
    24  
    25  type loopSuite struct {
    26  	testing.BaseSuite
    27  	storageDir       string
    28  	commands         *mockRunCommand
    29  	runningInsideLXC bool
    30  }
    31  
    32  func (s *loopSuite) SetUpTest(c *gc.C) {
    33  	s.BaseSuite.SetUpTest(c)
    34  	s.storageDir = c.MkDir()
    35  	s.runningInsideLXC = false
    36  }
    37  
    38  func (s *loopSuite) TearDownTest(c *gc.C) {
    39  	s.commands.assertDrained()
    40  	s.BaseSuite.TearDownTest(c)
    41  }
    42  
    43  func (s *loopSuite) loopProvider(c *gc.C) storage.Provider {
    44  	s.commands = &mockRunCommand{c: c}
    45  	runningInsideLXC := func() (bool, error) {
    46  		return s.runningInsideLXC, nil
    47  	}
    48  	return provider.LoopProvider(s.commands.run, runningInsideLXC)
    49  }
    50  
    51  func (s *loopSuite) TestVolumeSource(c *gc.C) {
    52  	p := s.loopProvider(c)
    53  	cfg, err := storage.NewConfig("name", provider.LoopProviderType, map[string]interface{}{})
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	_, err = p.VolumeSource(nil, cfg)
    56  	c.Assert(err, gc.ErrorMatches, "storage directory not specified")
    57  	cfg, err = storage.NewConfig("name", provider.LoopProviderType, map[string]interface{}{
    58  		"storage-dir": c.MkDir(),
    59  	})
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	_, err = p.VolumeSource(nil, cfg)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  }
    64  
    65  func (s *loopSuite) TestValidateConfig(c *gc.C) {
    66  	p := s.loopProvider(c)
    67  	cfg, err := storage.NewConfig("name", provider.LoopProviderType, map[string]interface{}{})
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	err = p.ValidateConfig(cfg)
    70  	// The loop provider does not have any user
    71  	// configuration, so an empty map will pass.
    72  	c.Assert(err, jc.ErrorIsNil)
    73  }
    74  
    75  func (s *loopSuite) TestSupports(c *gc.C) {
    76  	p := s.loopProvider(c)
    77  	c.Assert(p.Supports(storage.StorageKindBlock), jc.IsTrue)
    78  	c.Assert(p.Supports(storage.StorageKindFilesystem), jc.IsFalse)
    79  }
    80  
    81  func (s *loopSuite) TestScope(c *gc.C) {
    82  	p := s.loopProvider(c)
    83  	c.Assert(p.Scope(), gc.Equals, storage.ScopeMachine)
    84  }
    85  
    86  func (s *loopSuite) loopVolumeSource(c *gc.C) (storage.VolumeSource, *provider.MockDirFuncs) {
    87  	s.commands = &mockRunCommand{c: c}
    88  	return provider.LoopVolumeSource(
    89  		s.storageDir,
    90  		s.commands.run,
    91  		s.runningInsideLXC,
    92  	)
    93  }
    94  
    95  func (s *loopSuite) TestCreateVolumes(c *gc.C) {
    96  	source, _ := s.loopVolumeSource(c)
    97  	s.commands.expect("fallocate", "-l", "2MiB", filepath.Join(s.storageDir, "volume-0"))
    98  
    99  	volumes, volumeAttachments, err := source.CreateVolumes([]storage.VolumeParams{{
   100  		Tag:  names.NewVolumeTag("0"),
   101  		Size: 2,
   102  		Attachment: &storage.VolumeAttachmentParams{
   103  			AttachmentParams: storage.AttachmentParams{
   104  				Machine:    names.NewMachineTag("1"),
   105  				InstanceId: "instance-id",
   106  			},
   107  		},
   108  	}})
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	c.Assert(volumes, gc.HasLen, 1)
   111  	// volume attachments always deferred to AttachVolumes
   112  	c.Assert(volumeAttachments, gc.HasLen, 0)
   113  	c.Assert(volumes[0], gc.Equals, storage.Volume{
   114  		names.NewVolumeTag("0"),
   115  		storage.VolumeInfo{
   116  			VolumeId: "volume-0",
   117  			Size:     2,
   118  		},
   119  	})
   120  }
   121  
   122  func (s *loopSuite) TestCreateVolumesNoAttachment(c *gc.C) {
   123  	source, _ := s.loopVolumeSource(c)
   124  	s.commands.expect("fallocate", "-l", "2MiB", filepath.Join(s.storageDir, "volume-0"))
   125  	_, _, err := source.CreateVolumes([]storage.VolumeParams{{
   126  		Tag:  names.NewVolumeTag("0"),
   127  		Size: 2,
   128  	}})
   129  	// loop volumes may be created without attachments
   130  	c.Assert(err, jc.ErrorIsNil)
   131  }
   132  
   133  func (s *loopSuite) TestCreateVolumesInsideLXC(c *gc.C) {
   134  	s.runningInsideLXC = true
   135  
   136  	source, _ := s.loopVolumeSource(c)
   137  	s.testCreateVolumesInsideLXC(c, source)
   138  
   139  	p := s.loopProvider(c)
   140  	cfg, err := storage.NewConfig("name", provider.LoopProviderType, map[string]interface{}{
   141  		"storage-dir": s.storageDir,
   142  	})
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	source, err = p.VolumeSource(nil, cfg)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	s.testCreateVolumesInsideLXC(c, source)
   147  }
   148  
   149  func (s *loopSuite) testCreateVolumesInsideLXC(c *gc.C, source storage.VolumeSource) {
   150  	s.commands.expect("fallocate", "-l", "2MiB", filepath.Join(s.storageDir, "volume-0"))
   151  	volumes, _, err := source.CreateVolumes([]storage.VolumeParams{{
   152  		Tag: names.NewVolumeTag("0"), Size: 2,
   153  	}})
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	c.Assert(volumes, gc.HasLen, 1)
   156  	c.Assert(volumes[0].VolumeInfo.Persistent, jc.IsTrue)
   157  }
   158  
   159  func (s *loopSuite) TestDestroyVolumes(c *gc.C) {
   160  	source, _ := s.loopVolumeSource(c)
   161  	fileName := filepath.Join(s.storageDir, "volume-0")
   162  
   163  	err := ioutil.WriteFile(fileName, nil, 0644)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  
   166  	errs := source.DestroyVolumes([]string{"volume-0"})
   167  	c.Assert(errs, gc.HasLen, 1)
   168  	c.Assert(errs[0], jc.ErrorIsNil)
   169  
   170  	_, err = os.Stat(fileName)
   171  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   172  }
   173  
   174  func (s *loopSuite) TestDestroyVolumesInvalidVolumeId(c *gc.C) {
   175  	source, _ := s.loopVolumeSource(c)
   176  	errs := source.DestroyVolumes([]string{"../super/important/stuff"})
   177  	c.Assert(errs, gc.HasLen, 1)
   178  	c.Assert(errs[0], gc.ErrorMatches, `.* invalid loop volume ID "\.\./super/important/stuff"`)
   179  }
   180  
   181  func (s *loopSuite) TestDescribeVolumes(c *gc.C) {
   182  	source, _ := s.loopVolumeSource(c)
   183  	_, err := source.DescribeVolumes([]string{"a", "b"})
   184  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
   185  }
   186  
   187  func (s *loopSuite) TestAttachVolumes(c *gc.C) {
   188  	source, _ := s.loopVolumeSource(c)
   189  	cmd := s.commands.expect("losetup", "-j", filepath.Join(s.storageDir, "volume-0"))
   190  	cmd.respond("", nil) // no existing attachment
   191  	cmd = s.commands.expect("losetup", "-f", "--show", filepath.Join(s.storageDir, "volume-0"))
   192  	cmd.respond("/dev/loop98", nil) // first available loop device
   193  	cmd = s.commands.expect("losetup", "-j", filepath.Join(s.storageDir, "volume-1"))
   194  	cmd.respond("", nil) // no existing attachment
   195  	cmd = s.commands.expect("losetup", "-f", "--show", "-r", filepath.Join(s.storageDir, "volume-1"))
   196  	cmd.respond("/dev/loop99", nil)
   197  	cmd = s.commands.expect("losetup", "-j", filepath.Join(s.storageDir, "volume-2"))
   198  	cmd.respond("/dev/loop42: foo\n/dev/loop1: foo\n", nil) // existing attachments
   199  
   200  	volumeAttachments, err := source.AttachVolumes([]storage.VolumeAttachmentParams{{
   201  		Volume:   names.NewVolumeTag("0"),
   202  		VolumeId: "vol-ume0",
   203  		AttachmentParams: storage.AttachmentParams{
   204  			Machine:    names.NewMachineTag("0"),
   205  			InstanceId: "inst-ance",
   206  		},
   207  	}, {
   208  		Volume:   names.NewVolumeTag("1"),
   209  		VolumeId: "vol-ume1",
   210  		AttachmentParams: storage.AttachmentParams{
   211  			Machine:    names.NewMachineTag("0"),
   212  			InstanceId: "inst-ance",
   213  			ReadOnly:   true,
   214  		},
   215  	}, {
   216  		Volume:   names.NewVolumeTag("2"),
   217  		VolumeId: "vol-ume2",
   218  		AttachmentParams: storage.AttachmentParams{
   219  			Machine:    names.NewMachineTag("0"),
   220  			InstanceId: "inst-ance",
   221  		},
   222  	}})
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	c.Assert(volumeAttachments, jc.DeepEquals, []storage.VolumeAttachment{{
   225  		names.NewVolumeTag("0"),
   226  		names.NewMachineTag("0"),
   227  		storage.VolumeAttachmentInfo{
   228  			DeviceName: "loop98",
   229  		},
   230  	}, {
   231  		names.NewVolumeTag("1"),
   232  		names.NewMachineTag("0"),
   233  		storage.VolumeAttachmentInfo{
   234  			DeviceName: "loop99",
   235  			ReadOnly:   true,
   236  		},
   237  	}, {
   238  		names.NewVolumeTag("2"),
   239  		names.NewMachineTag("0"),
   240  		storage.VolumeAttachmentInfo{
   241  			DeviceName: "loop42",
   242  		},
   243  	}})
   244  }
   245  
   246  func (s *loopSuite) TestDetachVolumes(c *gc.C) {
   247  	source, _ := s.loopVolumeSource(c)
   248  	fileName := filepath.Join(s.storageDir, "volume-0")
   249  	cmd := s.commands.expect("losetup", "-j", fileName)
   250  	cmd.respond("/dev/loop0: foo\n/dev/loop1: bar\n", nil)
   251  	s.commands.expect("losetup", "-d", "/dev/loop0")
   252  	s.commands.expect("losetup", "-d", "/dev/loop1")
   253  
   254  	err := ioutil.WriteFile(fileName, nil, 0644)
   255  	c.Assert(err, jc.ErrorIsNil)
   256  
   257  	err = source.DetachVolumes([]storage.VolumeAttachmentParams{{
   258  		Volume:   names.NewVolumeTag("0"),
   259  		VolumeId: "vol-ume0",
   260  		AttachmentParams: storage.AttachmentParams{
   261  			Machine:    names.NewMachineTag("0"),
   262  			InstanceId: "inst-ance",
   263  		},
   264  	}})
   265  	c.Assert(err, jc.ErrorIsNil)
   266  
   267  	// file should not have been removed
   268  	_, err = os.Stat(fileName)
   269  	c.Assert(err, jc.ErrorIsNil)
   270  }
   271  
   272  func (s *loopSuite) TestDetachVolumesDetachFails(c *gc.C) {
   273  	source, _ := s.loopVolumeSource(c)
   274  	fileName := filepath.Join(s.storageDir, "volume-0")
   275  	cmd := s.commands.expect("losetup", "-j", fileName)
   276  	cmd.respond("/dev/loop0: foo\n/dev/loop1: bar\n", nil)
   277  	cmd = s.commands.expect("losetup", "-d", "/dev/loop0")
   278  	cmd.respond("", errors.New("oy"))
   279  
   280  	err := ioutil.WriteFile(fileName, nil, 0644)
   281  	c.Assert(err, jc.ErrorIsNil)
   282  
   283  	err = source.DetachVolumes([]storage.VolumeAttachmentParams{{
   284  		Volume:   names.NewVolumeTag("0"),
   285  		VolumeId: "vol-ume0",
   286  		AttachmentParams: storage.AttachmentParams{
   287  			Machine:    names.NewMachineTag("0"),
   288  			InstanceId: "inst-ance",
   289  		},
   290  	}})
   291  	c.Assert(err, gc.ErrorMatches, `.* detaching loop device "loop0": oy`)
   292  
   293  	// file should not have been removed
   294  	_, err = os.Stat(fileName)
   295  	c.Assert(err, jc.ErrorIsNil)
   296  }