github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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  	filesystems, 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(filesystems, jc.DeepEquals, []storage.Filesystem{{
   108  		Tag: names.NewFilesystemTag("6"),
   109  		FilesystemInfo: storage.FilesystemInfo{
   110  			FilesystemId: "6",
   111  			Size:         2,
   112  		},
   113  	}, {
   114  		Tag: names.NewFilesystemTag("7"),
   115  		FilesystemInfo: storage.FilesystemInfo{
   116  			FilesystemId: "7",
   117  			Size:         4,
   118  		},
   119  	}})
   120  }
   121  
   122  func (s *rootfsSuite) TestCreateFilesystemsIsUse(c *gc.C) {
   123  	source := s.rootfsFilesystemSource(c)
   124  	_, err := source.CreateFilesystems([]storage.FilesystemParams{{
   125  		Tag:  names.NewFilesystemTag("666"), // magic; see mockDirFuncs
   126  		Size: 1,
   127  	}})
   128  	c.Assert(err, gc.ErrorMatches, "creating filesystem: \".*/666\" is not empty")
   129  }
   130  
   131  func (s *rootfsSuite) TestAttachFilesystemsPathNotDir(c *gc.C) {
   132  	source := s.rootfsFilesystemSource(c)
   133  	_, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   134  		Filesystem:   names.NewFilesystemTag("6"),
   135  		FilesystemId: "6",
   136  		Path:         "file",
   137  	}})
   138  	c.Assert(err, gc.ErrorMatches, `.* path "file" must be a directory`)
   139  }
   140  
   141  func (s *rootfsSuite) TestCreateFilesystemsNotEnoughSpace(c *gc.C) {
   142  	source := s.rootfsFilesystemSource(c)
   143  	cmd := s.commands.expect("df", "--output=size", s.storageDir)
   144  	cmd.respond("1K-blocks\n2048", nil)
   145  
   146  	_, err := source.CreateFilesystems([]storage.FilesystemParams{{
   147  		Tag:  names.NewFilesystemTag("6"),
   148  		Size: 4,
   149  	}})
   150  	c.Assert(err, gc.ErrorMatches, ".* filesystem is not big enough \\(2M < 4M\\)")
   151  }
   152  
   153  func (s *rootfsSuite) TestCreateFilesystemsInvalidPath(c *gc.C) {
   154  	source := s.rootfsFilesystemSource(c)
   155  	cmd := s.commands.expect("df", "--output=size", s.storageDir)
   156  	cmd.respond("", errors.New("error creating directory"))
   157  
   158  	_, err := source.CreateFilesystems([]storage.FilesystemParams{{
   159  		Tag:  names.NewFilesystemTag("6"),
   160  		Size: 2,
   161  	}})
   162  	c.Assert(err, gc.ErrorMatches, ".* error creating directory")
   163  }
   164  
   165  func (s *rootfsSuite) TestAttachFilesystemsNoPathSpecified(c *gc.C) {
   166  	source := s.rootfsFilesystemSource(c)
   167  	_, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   168  		Filesystem:   names.NewFilesystemTag("6"),
   169  		FilesystemId: "6",
   170  	}})
   171  	c.Assert(err, gc.ErrorMatches, "attaching filesystem 6: filesystem mount point not specified")
   172  }
   173  
   174  func (s *rootfsSuite) TestAttachFilesystemsBind(c *gc.C) {
   175  	source := s.rootfsFilesystemSource(c)
   176  
   177  	cmd := s.commands.expect("df", "--output=source", "/srv")
   178  	cmd.respond("headers\n/src/of/root", nil)
   179  
   180  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv")
   181  	cmd.respond("", nil)
   182  
   183  	info, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   184  		Filesystem:   names.NewFilesystemTag("6"),
   185  		FilesystemId: "6",
   186  		Path:         "/srv",
   187  	}})
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	c.Assert(info, jc.DeepEquals, []storage.FilesystemAttachment{{
   190  		Filesystem: names.NewFilesystemTag("6"),
   191  		FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   192  			Path: "/srv",
   193  		},
   194  	}})
   195  }
   196  
   197  func (s *rootfsSuite) TestAttachFilesystemsBound(c *gc.C) {
   198  	source := s.rootfsFilesystemSource(c)
   199  
   200  	// already bind-mounted storage-dir/6 to the target
   201  	cmd := s.commands.expect("df", "--output=source", "/srv")
   202  	cmd.respond("headers\n"+filepath.Join(s.storageDir, "6"), nil)
   203  
   204  	info, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   205  		Filesystem:   names.NewFilesystemTag("6"),
   206  		FilesystemId: "6",
   207  		Path:         "/srv",
   208  	}})
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	c.Assert(info, jc.DeepEquals, []storage.FilesystemAttachment{{
   211  		Filesystem: names.NewFilesystemTag("6"),
   212  		FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   213  			Path: "/srv",
   214  		},
   215  	}})
   216  }
   217  
   218  func (s *rootfsSuite) TestAttachFilesystemsBindFailsDifferentFS(c *gc.C) {
   219  	source := s.rootfsFilesystemSource(c)
   220  
   221  	cmd := s.commands.expect("df", "--output=source", "/srv")
   222  	cmd.respond("headers\n/src/of/root", nil)
   223  
   224  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv")
   225  	cmd.respond("", errors.New("mount --bind fails"))
   226  
   227  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   228  	cmd.respond("headers\n/dev", nil)
   229  
   230  	cmd = s.commands.expect("df", "--output=target", "/srv")
   231  	cmd.respond("headers\n/proc", nil)
   232  
   233  	_, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   234  		Filesystem:   names.NewFilesystemTag("6"),
   235  		FilesystemId: "6",
   236  		Path:         "/srv",
   237  	}})
   238  	c.Assert(err, gc.ErrorMatches, `attaching filesystem 6: ".*/6" \("/dev"\) and "/srv" \("/proc"\) are on different filesystems`)
   239  }
   240  
   241  func (s *rootfsSuite) TestAttachFilesystemsBindSameFSEmptyDir(c *gc.C) {
   242  	source := s.rootfsFilesystemSource(c)
   243  
   244  	cmd := s.commands.expect("df", "--output=source", "/srv")
   245  	cmd.respond("headers\n/src/of/root", nil)
   246  
   247  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv")
   248  	cmd.respond("", errors.New("mount --bind fails"))
   249  
   250  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   251  	cmd.respond("headers\n/dev", nil)
   252  
   253  	cmd = s.commands.expect("df", "--output=target", "/srv")
   254  	cmd.respond("headers\n/dev", nil)
   255  
   256  	info, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   257  		Filesystem:   names.NewFilesystemTag("6"),
   258  		FilesystemId: "6",
   259  		Path:         "/srv",
   260  	}})
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(info, jc.DeepEquals, []storage.FilesystemAttachment{{
   263  		Filesystem: names.NewFilesystemTag("6"),
   264  		FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   265  			Path: "/srv",
   266  		},
   267  	}})
   268  }
   269  
   270  func (s *rootfsSuite) TestAttachFilesystemsBindSameFSNonEmptyDirUnclaimed(c *gc.C) {
   271  	source := s.rootfsFilesystemSource(c)
   272  
   273  	cmd := s.commands.expect("df", "--output=source", "/srv/666")
   274  	cmd.respond("headers\n/src/of/root", nil)
   275  
   276  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv/666")
   277  	cmd.respond("", errors.New("mount --bind fails"))
   278  
   279  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   280  	cmd.respond("headers\n/dev", nil)
   281  
   282  	cmd = s.commands.expect("df", "--output=target", "/srv/666")
   283  	cmd.respond("headers\n/dev", nil)
   284  
   285  	_, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   286  		Filesystem:   names.NewFilesystemTag("6"),
   287  		FilesystemId: "6",
   288  		Path:         "/srv/666",
   289  	}})
   290  	c.Assert(err, gc.ErrorMatches, `attaching filesystem 6: "/srv/666" is not empty`)
   291  }
   292  
   293  func (s *rootfsSuite) TestAttachFilesystemsBindSameFSNonEmptyDirClaimed(c *gc.C) {
   294  	source := s.rootfsFilesystemSource(c)
   295  
   296  	cmd := s.commands.expect("df", "--output=source", "/srv/666")
   297  	cmd.respond("headers\n/src/of/root", nil)
   298  
   299  	cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv/666")
   300  	cmd.respond("", errors.New("mount --bind fails"))
   301  
   302  	cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6"))
   303  	cmd.respond("headers\n/dev", nil)
   304  
   305  	cmd = s.commands.expect("df", "--output=target", "/srv/666")
   306  	cmd.respond("headers\n/dev", nil)
   307  
   308  	s.mockDirFuncs.Dirs.Add(filepath.Join(s.storageDir, "6", "juju-target-claimed"))
   309  
   310  	info, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{
   311  		Filesystem:   names.NewFilesystemTag("6"),
   312  		FilesystemId: "6",
   313  		Path:         "/srv/666",
   314  	}})
   315  	c.Assert(err, jc.ErrorIsNil)
   316  	c.Assert(info, jc.DeepEquals, []storage.FilesystemAttachment{{
   317  		Filesystem: names.NewFilesystemTag("6"),
   318  		FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{
   319  			Path: "/srv/666",
   320  		},
   321  	}})
   322  }
   323  
   324  func (s *rootfsSuite) TestDetachFilesystems(c *gc.C) {
   325  	source := s.rootfsFilesystemSource(c)
   326  	testDetachFilesystems(c, s.commands, source, true)
   327  }
   328  
   329  func (s *rootfsSuite) TestDetachFilesystemsUnattached(c *gc.C) {
   330  	// The "unattached" case covers both idempotency, and
   331  	// also the scenario where bind-mounting failed. In
   332  	// either case, there is no attachment-specific filesystem
   333  	// mount.
   334  	source := s.rootfsFilesystemSource(c)
   335  	testDetachFilesystems(c, s.commands, source, false)
   336  }