github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/storage/provider/rootfs_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  	"errors"
     8  	"path/filepath"
     9  	"runtime"
    10  
    11  	"github.com/juju/names"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/storage"
    16  	"github.com/juju/juju/storage/provider"
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  var _ = gc.Suite(&rootfsSuite{})
    21  
    22  type rootfsSuite struct {
    23  	testing.BaseSuite
    24  	storageDir   string
    25  	commands     *mockRunCommand
    26  	mockDirFuncs *provider.MockDirFuncs
    27  }
    28  
    29  func (s *rootfsSuite) SetUpTest(c *gc.C) {
    30  	if runtime.GOOS == "windows" {
    31  		c.Skip("Tests relevant only on *nix systems")
    32  	}
    33  	s.BaseSuite.SetUpTest(c)
    34  	s.storageDir = c.MkDir()
    35  }
    36  
    37  func (s *rootfsSuite) TearDownTest(c *gc.C) {
    38  	if s.commands != nil {
    39  		s.commands.assertDrained()
    40  	}
    41  	s.BaseSuite.TearDownTest(c)
    42  }
    43  
    44  func (s *rootfsSuite) rootfsProvider(c *gc.C) storage.Provider {
    45  	s.commands = &mockRunCommand{c: c}
    46  	return provider.RootfsProvider(s.commands.run)
    47  }
    48  
    49  func (s *rootfsSuite) TestFilesystemSource(c *gc.C) {
    50  	p := s.rootfsProvider(c)
    51  	cfg, err := storage.NewConfig("name", provider.RootfsProviderType, map[string]interface{}{})
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	_, err = p.FilesystemSource(nil, cfg)
    54  	c.Assert(err, gc.ErrorMatches, "storage directory not specified")
    55  	cfg, err = storage.NewConfig("name", provider.RootfsProviderType, map[string]interface{}{
    56  		"storage-dir": c.MkDir(),
    57  	})
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	_, err = p.FilesystemSource(nil, cfg)
    60  	c.Assert(err, jc.ErrorIsNil)
    61  }
    62  
    63  func (s *rootfsSuite) TestValidateConfig(c *gc.C) {
    64  	p := s.rootfsProvider(c)
    65  	cfg, err := storage.NewConfig("name", provider.RootfsProviderType, map[string]interface{}{})
    66  	c.Assert(err, jc.ErrorIsNil)
    67  	err = p.ValidateConfig(cfg)
    68  	// The rootfs provider does not have any user
    69  	// configuration, so an empty map will pass.
    70  	c.Assert(err, jc.ErrorIsNil)
    71  }
    72  
    73  func (s *rootfsSuite) TestSupports(c *gc.C) {
    74  	p := s.rootfsProvider(c)
    75  	c.Assert(p.Supports(storage.StorageKindBlock), jc.IsFalse)
    76  	c.Assert(p.Supports(storage.StorageKindFilesystem), jc.IsTrue)
    77  }
    78  
    79  func (s *rootfsSuite) TestScope(c *gc.C) {
    80  	p := s.rootfsProvider(c)
    81  	c.Assert(p.Scope(), gc.Equals, storage.ScopeMachine)
    82  }
    83  
    84  func (s *rootfsSuite) rootfsFilesystemSource(c *gc.C) storage.FilesystemSource {
    85  	s.commands = &mockRunCommand{c: c}
    86  	source, d := provider.RootfsFilesystemSource(s.storageDir, s.commands.run)
    87  	s.mockDirFuncs = d
    88  	return source
    89  }
    90  
    91  func (s *rootfsSuite) TestCreateFilesystems(c *gc.C) {
    92  	source := s.rootfsFilesystemSource(c)
    93  	cmd := s.commands.expect("df", "--output=size", s.storageDir)
    94  	cmd.respond("1K-blocks\n2048", nil)
    95  	cmd = s.commands.expect("df", "--output=size", s.storageDir)
    96  	cmd.respond("1K-blocks\n4096", nil)
    97  
    98  	results, err := source.CreateFilesystems([]storage.FilesystemParams{{
    99  		Tag:  names.NewFilesystemTag("6"),
   100  		Size: 2,
   101  	}, {
   102  		Tag:  names.NewFilesystemTag("7"),
   103  		Size: 4,
   104  	}})
   105  	c.Assert(err, jc.ErrorIsNil)
   106  
   107  	c.Assert(results, jc.DeepEquals, []storage.CreateFilesystemsResult{{
   108  		Filesystem: &storage.Filesystem{
   109  			Tag: names.NewFilesystemTag("6"),
   110  			FilesystemInfo: storage.FilesystemInfo{
   111  				FilesystemId: "6",
   112  				Size:         2,
   113  			},
   114  		},
   115  	}, {
   116  		Filesystem: &storage.Filesystem{
   117  			Tag: names.NewFilesystemTag("7"),
   118  			FilesystemInfo: storage.FilesystemInfo{
   119  				FilesystemId: "7",
   120  				Size:         4,
   121  			},
   122  		},
   123  	}})
   124  }
   125  
   126  func (s *rootfsSuite) TestCreateFilesystemsIsUse(c *gc.C) {
   127  	source := s.rootfsFilesystemSource(c)
   128  	results, err := source.CreateFilesystems([]storage.FilesystemParams{{
   129  		Tag:  names.NewFilesystemTag("666"), // magic; see mockDirFuncs
   130  		Size: 1,
   131  	}})
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	c.Assert(results[0].Error, gc.ErrorMatches, "\".*/666\" is not empty")
   134  }
   135  
   136  func (s *rootfsSuite) TestAttachFilesystemsPathNotDir(c *gc.C) {
   137  	source := s.rootfsFilesystemSource(c)
   138  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   139  		Filesystem:   names.NewFilesystemTag("6"),
   140  		FilesystemId: "6",
   141  		Path:         "file",
   142  	}})
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	c.Assert(results[0].Error, gc.ErrorMatches, `path "file" must be a directory`)
   145  }
   146  
   147  func (s *rootfsSuite) TestCreateFilesystemsNotEnoughSpace(c *gc.C) {
   148  	source := s.rootfsFilesystemSource(c)
   149  	cmd := s.commands.expect("df", "--output=size", s.storageDir)
   150  	cmd.respond("1K-blocks\n2048", nil)
   151  
   152  	results, err := source.CreateFilesystems([]storage.FilesystemParams{{
   153  		Tag:  names.NewFilesystemTag("6"),
   154  		Size: 4,
   155  	}})
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	c.Assert(results[0].Error, gc.ErrorMatches, "filesystem is not big enough \\(2M < 4M\\)")
   158  }
   159  
   160  func (s *rootfsSuite) TestCreateFilesystemsInvalidPath(c *gc.C) {
   161  	source := s.rootfsFilesystemSource(c)
   162  	cmd := s.commands.expect("df", "--output=size", s.storageDir)
   163  	cmd.respond("", errors.New("error creating directory"))
   164  
   165  	results, err := source.CreateFilesystems([]storage.FilesystemParams{{
   166  		Tag:  names.NewFilesystemTag("6"),
   167  		Size: 2,
   168  	}})
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	c.Assert(results[0].Error, gc.ErrorMatches, "getting size: error creating directory")
   171  }
   172  
   173  func (s *rootfsSuite) TestAttachFilesystemsNoPathSpecified(c *gc.C) {
   174  	source := s.rootfsFilesystemSource(c)
   175  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   176  		Filesystem:   names.NewFilesystemTag("6"),
   177  		FilesystemId: "6",
   178  	}})
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	c.Assert(results[0].Error, gc.ErrorMatches, "filesystem mount point not specified")
   181  }
   182  
   183  func (s *rootfsSuite) TestAttachFilesystemsBind(c *gc.C) {
   184  	source := s.rootfsFilesystemSource(c)
   185  
   186  	cmd := s.commands.expect("df", "--output=source", "/srv")
   187  	cmd.respond("headers\n/src/of/root", nil)
   188  
   189  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv")
   190  	cmd.respond("", nil)
   191  
   192  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   193  		Filesystem:   names.NewFilesystemTag("6"),
   194  		FilesystemId: "6",
   195  		Path:         "/srv",
   196  	}})
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{
   199  		FilesystemAttachment: &storage.FilesystemAttachment{
   200  			Filesystem: names.NewFilesystemTag("6"),
   201  			FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   202  				Path: "/srv",
   203  			},
   204  		},
   205  	}})
   206  }
   207  
   208  func (s *rootfsSuite) TestAttachFilesystemsBound(c *gc.C) {
   209  	source := s.rootfsFilesystemSource(c)
   210  
   211  	// already bind-mounted storage-dir/6 to the target
   212  	cmd := s.commands.expect("df", "--output=source", "/srv")
   213  	cmd.respond("headers\n"+filepath.Join(s.storageDir, "6"), nil)
   214  
   215  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   216  		Filesystem:   names.NewFilesystemTag("6"),
   217  		FilesystemId: "6",
   218  		Path:         "/srv",
   219  	}})
   220  	c.Assert(err, jc.ErrorIsNil)
   221  	c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{
   222  		FilesystemAttachment: &storage.FilesystemAttachment{
   223  			Filesystem: names.NewFilesystemTag("6"),
   224  			FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   225  				Path: "/srv",
   226  			},
   227  		},
   228  	}})
   229  }
   230  
   231  func (s *rootfsSuite) TestAttachFilesystemsBindFailsDifferentFS(c *gc.C) {
   232  	source := s.rootfsFilesystemSource(c)
   233  
   234  	cmd := s.commands.expect("df", "--output=source", "/srv")
   235  	cmd.respond("headers\n/src/of/root", nil)
   236  
   237  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv")
   238  	cmd.respond("", errors.New("mount --bind fails"))
   239  
   240  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   241  	cmd.respond("headers\n/dev", nil)
   242  
   243  	cmd = s.commands.expect("df", "--output=target", "/srv")
   244  	cmd.respond("headers\n/proc", nil)
   245  
   246  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   247  		Filesystem:   names.NewFilesystemTag("6"),
   248  		FilesystemId: "6",
   249  		Path:         "/srv",
   250  	}})
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	c.Assert(results[0].Error, gc.ErrorMatches, `".*/6" \("/dev"\) and "/srv" \("/proc"\) are on different filesystems`)
   253  }
   254  
   255  func (s *rootfsSuite) TestAttachFilesystemsBindSameFSEmptyDir(c *gc.C) {
   256  	source := s.rootfsFilesystemSource(c)
   257  
   258  	cmd := s.commands.expect("df", "--output=source", "/srv")
   259  	cmd.respond("headers\n/src/of/root", nil)
   260  
   261  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv")
   262  	cmd.respond("", errors.New("mount --bind fails"))
   263  
   264  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   265  	cmd.respond("headers\n/dev", nil)
   266  
   267  	cmd = s.commands.expect("df", "--output=target", "/srv")
   268  	cmd.respond("headers\n/dev", nil)
   269  
   270  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   271  		Filesystem:   names.NewFilesystemTag("6"),
   272  		FilesystemId: "6",
   273  		Path:         "/srv",
   274  	}})
   275  	c.Assert(err, jc.ErrorIsNil)
   276  	c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{
   277  		FilesystemAttachment: &storage.FilesystemAttachment{
   278  			Filesystem: names.NewFilesystemTag("6"),
   279  			FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   280  				Path: "/srv",
   281  			},
   282  		},
   283  	}})
   284  }
   285  
   286  func (s *rootfsSuite) TestAttachFilesystemsBindSameFSNonEmptyDirUnclaimed(c *gc.C) {
   287  	source := s.rootfsFilesystemSource(c)
   288  
   289  	cmd := s.commands.expect("df", "--output=source", "/srv/666")
   290  	cmd.respond("headers\n/src/of/root", nil)
   291  
   292  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv/666")
   293  	cmd.respond("", errors.New("mount --bind fails"))
   294  
   295  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   296  	cmd.respond("headers\n/dev", nil)
   297  
   298  	cmd = s.commands.expect("df", "--output=target", "/srv/666")
   299  	cmd.respond("headers\n/dev", nil)
   300  
   301  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   302  		Filesystem:   names.NewFilesystemTag("6"),
   303  		FilesystemId: "6",
   304  		Path:         "/srv/666",
   305  	}})
   306  	c.Assert(err, jc.ErrorIsNil)
   307  	c.Assert(results[0].Error, gc.ErrorMatches, `"/srv/666" is not empty`)
   308  }
   309  
   310  func (s *rootfsSuite) TestAttachFilesystemsBindSameFSNonEmptyDirClaimed(c *gc.C) {
   311  	source := s.rootfsFilesystemSource(c)
   312  
   313  	cmd := s.commands.expect("df", "--output=source", "/srv/666")
   314  	cmd.respond("headers\n/src/of/root", nil)
   315  
   316  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv/666")
   317  	cmd.respond("", errors.New("mount --bind fails"))
   318  
   319  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   320  	cmd.respond("headers\n/dev", nil)
   321  
   322  	cmd = s.commands.expect("df", "--output=target", "/srv/666")
   323  	cmd.respond("headers\n/dev", nil)
   324  
   325  	s.mockDirFuncs.Dirs.Add(filepath.Join(s.storageDir, "6", "juju-target-claimed"))
   326  
   327  	results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   328  		Filesystem:   names.NewFilesystemTag("6"),
   329  		FilesystemId: "6",
   330  		Path:         "/srv/666",
   331  	}})
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{
   334  		FilesystemAttachment: &storage.FilesystemAttachment{
   335  			Filesystem: names.NewFilesystemTag("6"),
   336  			FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   337  				Path: "/srv/666",
   338  			},
   339  		},
   340  	}})
   341  }
   342  
   343  func (s *rootfsSuite) TestDetachFilesystems(c *gc.C) {
   344  	source := s.rootfsFilesystemSource(c)
   345  	testDetachFilesystems(c, s.commands, source, true)
   346  }
   347  
   348  func (s *rootfsSuite) TestDetachFilesystemsUnattached(c *gc.C) {
   349  	// The "unattached" case covers both idempotency, and
   350  	// also the scenario where bind-mounting failed. In
   351  	// either case, there is no attachment-specific filesystem
   352  	// mount.
   353  	source := s.rootfsFilesystemSource(c)
   354  	testDetachFilesystems(c, s.commands, source, false)
   355  }