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 }