github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/storage/provider/managedfs_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  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/names/v5"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/environs/context"
    16  	"github.com/juju/juju/storage"
    17  	"github.com/juju/juju/storage/provider"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  var _ = gc.Suite(&managedfsSuite{})
    22  
    23  type managedfsSuite struct {
    24  	testing.BaseSuite
    25  	commands     *mockRunCommand
    26  	dirFuncs     *provider.MockDirFuncs
    27  	blockDevices map[names.VolumeTag]storage.BlockDevice
    28  	filesystems  map[names.FilesystemTag]storage.Filesystem
    29  	fakeEtcDir   string
    30  
    31  	callCtx context.ProviderCallContext
    32  }
    33  
    34  func (s *managedfsSuite) SetUpTest(c *gc.C) {
    35  	s.BaseSuite.SetUpTest(c)
    36  	s.blockDevices = make(map[names.VolumeTag]storage.BlockDevice)
    37  	s.filesystems = make(map[names.FilesystemTag]storage.Filesystem)
    38  	s.callCtx = context.NewEmptyCloudCallContext()
    39  	s.fakeEtcDir = c.MkDir()
    40  }
    41  
    42  func (s *managedfsSuite) TearDownTest(c *gc.C) {
    43  	if s.commands != nil {
    44  		s.commands.assertDrained()
    45  	}
    46  	s.BaseSuite.TearDownTest(c)
    47  }
    48  
    49  func (s *managedfsSuite) initSource(c *gc.C, fakeMountInfo ...string) storage.FilesystemSource {
    50  	s.commands = &mockRunCommand{c: c}
    51  	source, mockDirFuncs := provider.NewMockManagedFilesystemSource(
    52  		s.fakeEtcDir,
    53  		s.commands.run,
    54  		s.blockDevices,
    55  		s.filesystems,
    56  		fakeMountInfo...,
    57  	)
    58  	s.dirFuncs = mockDirFuncs
    59  	return source
    60  }
    61  
    62  func (s *managedfsSuite) TestCreateFilesystems(c *gc.C) {
    63  	source := s.initSource(c)
    64  	// sda is (re)partitioned and the filesystem created
    65  	// on the partition.
    66  	s.commands.expect("sgdisk", "--zap-all", "/dev/sda")
    67  	s.commands.expect("sgdisk", "-n", "1:0:-1", "/dev/sda")
    68  	s.commands.expect("mkfs.ext4", "/dev/sda1")
    69  	// xvdf1 is assumed to not require a partition, on
    70  	// account of ending with a digit.
    71  	s.commands.expect("mkfs.ext4", "/dev/xvdf1")
    72  
    73  	s.blockDevices[names.NewVolumeTag("0")] = storage.BlockDevice{
    74  		DeviceName: "sda",
    75  		HardwareId: "capncrunch",
    76  		Size:       2,
    77  	}
    78  	s.blockDevices[names.NewVolumeTag("1")] = storage.BlockDevice{
    79  		DeviceName: "xvdf1",
    80  		HardwareId: "weetbix",
    81  		Size:       3,
    82  	}
    83  	results, err := source.CreateFilesystems(s.callCtx, []storage.FilesystemParams{{
    84  		Tag:    names.NewFilesystemTag("0/0"),
    85  		Volume: names.NewVolumeTag("0"),
    86  		Size:   2,
    87  	}, {
    88  		Tag:    names.NewFilesystemTag("0/1"),
    89  		Volume: names.NewVolumeTag("1"),
    90  		Size:   3,
    91  	}})
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	c.Assert(results, jc.DeepEquals, []storage.CreateFilesystemsResult{{
    94  		Filesystem: &storage.Filesystem{
    95  			names.NewFilesystemTag("0/0"),
    96  			names.NewVolumeTag("0"),
    97  			storage.FilesystemInfo{
    98  				FilesystemId: "filesystem-0-0",
    99  				Size:         2,
   100  			},
   101  		},
   102  	}, {
   103  		Filesystem: &storage.Filesystem{
   104  			names.NewFilesystemTag("0/1"),
   105  			names.NewVolumeTag("1"),
   106  			storage.FilesystemInfo{
   107  				FilesystemId: "filesystem-0-1",
   108  				Size:         3,
   109  			},
   110  		},
   111  	}})
   112  }
   113  
   114  func (s *managedfsSuite) TestCreateFilesystemsNoBlockDevice(c *gc.C) {
   115  	source := s.initSource(c)
   116  	results, err := source.CreateFilesystems(s.callCtx, []storage.FilesystemParams{{
   117  		Tag:    names.NewFilesystemTag("0/0"),
   118  		Volume: names.NewVolumeTag("0"),
   119  		Size:   2,
   120  	}})
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	c.Assert(results[0].Error, gc.ErrorMatches, "backing-volume 0 is not yet attached")
   123  }
   124  
   125  const testMountPoint = "/in/the/place"
   126  
   127  func mountInfoLine(id, parent int, root, mountPoint, source string) string {
   128  	return fmt.Sprintf("%d %d 8:1 %s %s rw,relatime shared:1 - ext4 %s rw,errors=remount-ro", id, parent, root, mountPoint, source)
   129  }
   130  
   131  func (s *managedfsSuite) TestAttachFilesystems(c *gc.C) {
   132  	nonRelatedFstabEntry := "/dev/foo /mount/point stuff"
   133  	err := os.WriteFile(filepath.Join(s.fakeEtcDir, "fstab"), []byte(nonRelatedFstabEntry), 0644)
   134  	c.Assert(err, jc.ErrorIsNil)
   135  
   136  	mtabEntry := fmt.Sprintf("/dev/sda1 %s other relatime 0 0", testMountPoint)
   137  	fstabEntry := fmt.Sprintf("/dev/sda1 %s other nofail,relatime 0 0", testMountPoint)
   138  	s.testAttachFilesystems(c, false, false, "", mtabEntry, nonRelatedFstabEntry+"\n"+fstabEntry+"\n")
   139  }
   140  
   141  func (s *managedfsSuite) TestAttachFilesystemsMissingMtab(c *gc.C) {
   142  	nonRelatedFstabEntry := "/dev/foo /mount/point stuff\n"
   143  	err := os.WriteFile(filepath.Join(s.fakeEtcDir, "fstab"), []byte(nonRelatedFstabEntry), 0644)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  
   146  	s.testAttachFilesystems(c, false, false, "", "", nonRelatedFstabEntry)
   147  }
   148  
   149  func (s *managedfsSuite) TestAttachFilesystemsExistingFstabEntry(c *gc.C) {
   150  	existingFstabEntry := fmt.Sprintf("/dev/sda1 %s existing mtab stuff\n", testMountPoint)
   151  	err := os.WriteFile(filepath.Join(s.fakeEtcDir, "fstab"), []byte(existingFstabEntry), 0644)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  
   154  	mtabEntry := fmt.Sprintf("/dev/sda1 %s other mtab stuff", testMountPoint)
   155  	s.testAttachFilesystems(c, false, false, "", mtabEntry, existingFstabEntry)
   156  }
   157  
   158  func (s *managedfsSuite) TestAttachFilesystemsUpdateExistingFstabEntryWithUUID(c *gc.C) {
   159  	existingFstabEntry := fmt.Sprintf("/dev/sda1 %s existing mtab stuff\n", testMountPoint)
   160  	err := os.WriteFile(filepath.Join(s.fakeEtcDir, "fstab"), []byte(existingFstabEntry), 0644)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  
   163  	expectedFstabEntry := fmt.Sprintf("# %s was on /dev/sda1 during installation\nUUID=deadbeaf %s other mtab,nofail stuff\n", testMountPoint, testMountPoint)
   164  	mtabEntry := fmt.Sprintf("/dev/sda1 %s other mtab stuff", testMountPoint)
   165  	s.testAttachFilesystems(c, false, false, "deadbeaf", mtabEntry, expectedFstabEntry)
   166  }
   167  
   168  func (s *managedfsSuite) TestAttachFilesystemsReadOnly(c *gc.C) {
   169  	mtabEntry := fmt.Sprintf("/dev/sda1 %s other nofail,relatime 0 0", testMountPoint)
   170  	s.testAttachFilesystems(c, true, false, "", mtabEntry, mtabEntry+"\n")
   171  }
   172  
   173  func (s *managedfsSuite) TestAttachFilesystemsReattach(c *gc.C) {
   174  	mtabEntry := fmt.Sprintf("/dev/sda1 %s other nofail,relatime 0 0", testMountPoint)
   175  	s.testAttachFilesystems(c, true, true, "", mtabEntry, "")
   176  }
   177  
   178  func (s *managedfsSuite) testAttachFilesystems(c *gc.C, readOnly, reattach bool, UUID, mtab, fstab string) {
   179  	mountInfo := ""
   180  	if reattach {
   181  		mountInfo = mountInfoLine(666, 0, "/different/to/rootfs", testMountPoint, "/dev/sda1")
   182  	}
   183  	source := s.initSource(c, mountInfo)
   184  
   185  	if mtab != "" {
   186  		err := os.WriteFile(filepath.Join(s.fakeEtcDir, "mtab"), []byte(mtab), 0644)
   187  		c.Assert(err, jc.ErrorIsNil)
   188  	}
   189  
   190  	if !reattach {
   191  		var args []string
   192  		if readOnly {
   193  			args = append(args, "-o", "ro")
   194  		}
   195  		args = append(args, "/dev/sda1", testMountPoint)
   196  		s.commands.expect("mount", args...)
   197  	}
   198  
   199  	s.blockDevices[names.NewVolumeTag("0")] = storage.BlockDevice{
   200  		DeviceName: "sda",
   201  		HardwareId: "capncrunch",
   202  		Size:       2,
   203  		UUID:       UUID,
   204  	}
   205  	s.filesystems[names.NewFilesystemTag("0/0")] = storage.Filesystem{
   206  		Tag:    names.NewFilesystemTag("0/0"),
   207  		Volume: names.NewVolumeTag("0"),
   208  	}
   209  
   210  	results, err := source.AttachFilesystems(s.callCtx, []storage.FilesystemAttachmentParams{{
   211  		Filesystem:   names.NewFilesystemTag("0/0"),
   212  		FilesystemId: "filesystem-0-0",
   213  		AttachmentParams: storage.AttachmentParams{
   214  			Machine:    names.NewMachineTag("0"),
   215  			InstanceId: "inst-ance",
   216  			ReadOnly:   readOnly,
   217  		},
   218  		Path: testMountPoint,
   219  	}})
   220  	c.Assert(err, jc.ErrorIsNil)
   221  	c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{
   222  		FilesystemAttachment: &storage.FilesystemAttachment{
   223  			names.NewFilesystemTag("0/0"),
   224  			names.NewMachineTag("0"),
   225  			storage.FilesystemAttachmentInfo{
   226  				Path:     testMountPoint,
   227  				ReadOnly: readOnly,
   228  			},
   229  		},
   230  	}})
   231  
   232  	if fstab != "" {
   233  		data, err := os.ReadFile(filepath.Join(s.fakeEtcDir, "fstab"))
   234  		c.Assert(err, jc.ErrorIsNil)
   235  		c.Assert(string(data), gc.Equals, fstab)
   236  	}
   237  }
   238  
   239  func (s *managedfsSuite) TestDetachFilesystems(c *gc.C) {
   240  	nonRelatedFstabEntry := "/dev/foo /mount/point stuff\n"
   241  	fstabEntry := fmt.Sprintf("%s %s other mtab stuff", "/dev/sda1", testMountPoint)
   242  	err := os.WriteFile(filepath.Join(s.fakeEtcDir, "fstab"), []byte(nonRelatedFstabEntry+fstabEntry), 0644)
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	mountInfo := mountInfoLine(666, 0, "/same/as/rootfs", testMountPoint, "/dev/sda1")
   245  	source := s.initSource(c, mountInfo)
   246  	testDetachFilesystems(c, s.commands, source, s.callCtx, true, s.fakeEtcDir, nonRelatedFstabEntry)
   247  }
   248  
   249  func (s *managedfsSuite) TestDetachFilesystemsUnattached(c *gc.C) {
   250  	source := s.initSource(c)
   251  	testDetachFilesystems(c, s.commands, source, s.callCtx, false, s.fakeEtcDir, "")
   252  }