github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/snap-bootstrap/initramfs_systemd_mount_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main_test 21 22 import ( 23 "fmt" 24 "path/filepath" 25 "time" 26 27 main "github.com/snapcore/snapd/cmd/snap-bootstrap" 28 "github.com/snapcore/snapd/dirs" 29 "github.com/snapcore/snapd/systemd" 30 "github.com/snapcore/snapd/testutil" 31 32 . "gopkg.in/check.v1" 33 ) 34 35 type doSystemdMountSuite struct { 36 testutil.BaseTest 37 } 38 39 var _ = Suite(&doSystemdMountSuite{}) 40 41 func (s *doSystemdMountSuite) SetUpTest(c *C) { 42 dirs.SetRootDir(c.MkDir()) 43 s.AddCleanup(func() { dirs.SetRootDir("") }) 44 } 45 46 func (s *doSystemdMountSuite) TestDoSystemdMountUnhappy(c *C) { 47 cmd := testutil.MockCommand(c, "systemd-mount", ` 48 echo "mocked error" 49 exit 1 50 `) 51 defer cmd.Restore() 52 53 err := main.DoSystemdMount("something", "somewhere only we know", nil) 54 c.Assert(err, ErrorMatches, "mocked error") 55 } 56 57 func (s *doSystemdMountSuite) TestDoSystemdMount(c *C) { 58 59 testStart := time.Now() 60 61 tt := []struct { 62 what string 63 where string 64 opts *main.SystemdMountOptions 65 timeNowTimes []time.Time 66 isMountedReturns []bool 67 expErr string 68 comment string 69 }{ 70 { 71 what: "/dev/sda3", 72 where: "/run/mnt/data", 73 timeNowTimes: []time.Time{testStart, testStart}, 74 isMountedReturns: []bool{true}, 75 comment: "happy default", 76 }, 77 { 78 what: "tmpfs", 79 where: "/run/mnt/data", 80 opts: &main.SystemdMountOptions{ 81 Tmpfs: true, 82 }, 83 timeNowTimes: []time.Time{testStart, testStart}, 84 isMountedReturns: []bool{true}, 85 comment: "happy tmpfs", 86 }, 87 { 88 what: "tmpfs", 89 where: "/run/mnt/data", 90 opts: &main.SystemdMountOptions{ 91 NeedsFsck: true, 92 }, 93 timeNowTimes: []time.Time{testStart, testStart}, 94 isMountedReturns: []bool{true}, 95 comment: "happy fsck", 96 }, 97 { 98 what: "tmpfs", 99 where: "/run/mnt/data", 100 opts: &main.SystemdMountOptions{ 101 Ephemeral: true, 102 }, 103 timeNowTimes: []time.Time{testStart, testStart}, 104 isMountedReturns: []bool{true}, 105 comment: "happy initramfs ephemeral", 106 }, 107 { 108 what: "tmpfs", 109 where: "/run/mnt/data", 110 opts: &main.SystemdMountOptions{ 111 NoWait: true, 112 }, 113 comment: "happy no wait", 114 }, 115 { 116 what: "what", 117 where: "where", 118 timeNowTimes: []time.Time{testStart, testStart, testStart, testStart.Add(2 * time.Minute)}, 119 isMountedReturns: []bool{false, false}, 120 expErr: "timed out after 1m30s waiting for mount what on where", 121 comment: "times out waiting for mount to appear", 122 }, 123 { 124 what: "what", 125 where: "where", 126 opts: &main.SystemdMountOptions{ 127 Tmpfs: true, 128 NeedsFsck: true, 129 }, 130 expErr: "cannot mount \"what\" at \"where\": impossible to fsck a tmpfs", 131 comment: "invalid tmpfs + fsck", 132 }, 133 } 134 135 for _, t := range tt { 136 comment := Commentf(t.comment) 137 138 var cleanups []func() 139 140 opts := t.opts 141 if opts == nil { 142 opts = &main.SystemdMountOptions{} 143 } 144 dirs.SetRootDir(c.MkDir()) 145 cleanups = append(cleanups, func() { dirs.SetRootDir("") }) 146 147 cmd := testutil.MockCommand(c, "systemd-mount", ``) 148 cleanups = append(cleanups, cmd.Restore) 149 150 timeCalls := 0 151 restore := main.MockTimeNow(func() time.Time { 152 timeCalls++ 153 c.Assert(timeCalls <= len(t.timeNowTimes), Equals, true, comment) 154 if timeCalls > len(t.timeNowTimes) { 155 c.Errorf("too many time.Now calls (%d)", timeCalls) 156 // we want the test to fail at some point and not run forever, so 157 // move time way forward to make it for sure time out 158 return testStart.Add(10000 * time.Hour) 159 } 160 return t.timeNowTimes[timeCalls-1] 161 }) 162 cleanups = append(cleanups, restore) 163 164 cleanups = append(cleanups, func() { 165 c.Assert(timeCalls, Equals, len(t.timeNowTimes), comment) 166 }) 167 168 isMountedCalls := 0 169 restore = main.MockOsutilIsMounted(func(where string) (bool, error) { 170 isMountedCalls++ 171 c.Assert(isMountedCalls <= len(t.isMountedReturns), Equals, true, comment) 172 if isMountedCalls > len(t.isMountedReturns) { 173 e := fmt.Sprintf("too many osutil.IsMounted calls (%d)", isMountedCalls) 174 c.Errorf(e) 175 // we want the test to fail at some point and not run forever, so 176 // move time way forward to make it for sure time out 177 return false, fmt.Errorf(e) 178 } 179 return t.isMountedReturns[isMountedCalls-1], nil 180 }) 181 cleanups = append(cleanups, restore) 182 183 cleanups = append(cleanups, func() { 184 c.Assert(isMountedCalls, Equals, len(t.isMountedReturns), comment) 185 }) 186 187 err := main.DoSystemdMount(t.what, t.where, t.opts) 188 if t.expErr != "" { 189 c.Assert(err, ErrorMatches, t.expErr) 190 } else { 191 c.Assert(err, IsNil) 192 193 args := []string{ 194 "systemd-mount", t.what, t.where, "--no-pager", "--no-ask-password", 195 } 196 if opts.Tmpfs { 197 args = append(args, "--type=tmpfs") 198 } 199 if opts.NeedsFsck { 200 args = append(args, "--fsck=yes") 201 } else { 202 args = append(args, "--fsck=no") 203 } 204 if opts.NoWait { 205 args = append(args, "--no-block") 206 } 207 c.Assert(cmd.Calls(), DeepEquals, [][]string{args}) 208 209 // check that the overrides are present if opts.Ephemeral is false, 210 // or check the overrides are not present if opts.Ephemeral is true 211 for _, initrdUnit := range []string{ 212 "initrd.target", 213 "initrd-fs.target", 214 "initrd-switch-root.target", 215 "local-fs.target", 216 } { 217 mountUnit := systemd.EscapeUnitNamePath(t.where) 218 fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit) 219 unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname) 220 if opts.Ephemeral { 221 c.Assert(unitFile, testutil.FileAbsent) 222 } else { 223 c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit] 224 Requires=%[1]s 225 After=%[1]s 226 `, mountUnit+".mount")) 227 } 228 } 229 } 230 231 for _, r := range cleanups { 232 r() 233 } 234 } 235 }