github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/backend/setup_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 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 backend_test 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 "strings" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/bootloader" 31 "github.com/snapcore/snapd/bootloader/bootloadertest" 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/overlord/snapstate/backend" 35 "github.com/snapcore/snapd/progress" 36 "github.com/snapcore/snapd/release" 37 "github.com/snapcore/snapd/snap" 38 "github.com/snapcore/snapd/snap/snaptest" 39 "github.com/snapcore/snapd/systemd" 40 "github.com/snapcore/snapd/testutil" 41 ) 42 43 type setupSuite struct { 44 testutil.BaseTest 45 be backend.Backend 46 umount *testutil.MockCmd 47 systemctlRestorer func() 48 } 49 50 var _ = Suite(&setupSuite{}) 51 52 func (s *setupSuite) SetUpTest(c *C) { 53 s.BaseTest.SetUpTest(c) 54 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 55 56 dirs.SetRootDir(c.MkDir()) 57 58 err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc", "systemd", "system", "multi-user.target.wants"), 0755) 59 c.Assert(err, IsNil) 60 61 s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 62 return []byte("ActiveState=inactive\n"), nil 63 }) 64 65 s.umount = testutil.MockCommand(c, "umount", "") 66 } 67 68 func (s *setupSuite) TearDownTest(c *C) { 69 s.BaseTest.TearDownTest(c) 70 dirs.SetRootDir("") 71 bootloader.Force(nil) 72 s.umount.Restore() 73 s.systemctlRestorer() 74 } 75 76 func (s *setupSuite) TestSetupDoUndoSimple(c *C) { 77 snapPath := makeTestSnap(c, helloYaml1) 78 79 si := snap.SideInfo{ 80 RealName: "hello", 81 Revision: snap.R(14), 82 } 83 84 snapType, err := s.be.SetupSnap(snapPath, "hello", &si, progress.Null) 85 c.Assert(err, IsNil) 86 c.Check(snapType, Equals, snap.TypeApp) 87 88 // after setup the snap file is in the right dir 89 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "hello_14.snap")), Equals, true) 90 91 // ensure the right unit is created 92 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "hello/14")) 93 c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s", filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "hello/14"))) 94 c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/hello_14.snap") 95 96 minInfo := snap.MinimalPlaceInfo("hello", snap.R(14)) 97 // mount dir was created 98 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, true) 99 100 // undo undoes the mount unit and the instdir creation 101 err = s.be.UndoSetupSnap(minInfo, "app", progress.Null) 102 c.Assert(err, IsNil) 103 104 l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) 105 c.Assert(l, HasLen, 0) 106 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, false) 107 108 c.Assert(osutil.FileExists(minInfo.MountFile()), Equals, false) 109 110 } 111 112 func (s *setupSuite) TestSetupDoUndoInstance(c *C) { 113 snapPath := makeTestSnap(c, helloYaml1) 114 115 si := snap.SideInfo{ 116 RealName: "hello", 117 Revision: snap.R(14), 118 } 119 120 snapType, err := s.be.SetupSnap(snapPath, "hello_instance", &si, progress.Null) 121 c.Assert(err, IsNil) 122 c.Check(snapType, Equals, snap.TypeApp) 123 124 // after setup the snap file is in the right dir 125 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "hello_instance_14.snap")), Equals, true) 126 127 // ensure the right unit is created 128 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "hello_instance/14")) 129 c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s", filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "hello_instance/14"))) 130 c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/hello_instance_14.snap") 131 132 minInfo := snap.MinimalPlaceInfo("hello_instance", snap.R(14)) 133 // mount dir was created 134 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, true) 135 136 // undo undoes the mount unit and the instdir creation 137 err = s.be.UndoSetupSnap(minInfo, "app", progress.Null) 138 c.Assert(err, IsNil) 139 140 l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) 141 c.Assert(l, HasLen, 0) 142 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, false) 143 144 c.Assert(osutil.FileExists(minInfo.MountFile()), Equals, false) 145 } 146 147 func (s *setupSuite) TestSetupDoUndoKernel(c *C) { 148 // kernel snaps only happen on non-classic 149 defer release.MockOnClassic(false)() 150 bloader := bootloadertest.Mock("mock", c.MkDir()) 151 bootloader.Force(bloader) 152 153 // we don't get real mounting 154 os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") 155 defer os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") 156 157 testFiles := [][]string{ 158 {"kernel.img", "kernel"}, 159 {"initrd.img", "initrd"}, 160 {"modules/4.4.0-14-generic/foo.ko", "a module"}, 161 {"firmware/bar.bin", "some firmware"}, 162 {"meta/kernel.yaml", "version: 4.2"}, 163 } 164 snapPath := snaptest.MakeTestSnapWithFiles(c, `name: kernel 165 version: 1.0 166 type: kernel 167 `, testFiles) 168 169 si := snap.SideInfo{ 170 RealName: "kernel", 171 Revision: snap.R(140), 172 } 173 174 snapType, err := s.be.SetupSnap(snapPath, "kernel", &si, progress.Null) 175 c.Assert(err, IsNil) 176 c.Check(snapType, Equals, snap.TypeKernel) 177 c.Assert(bloader.ExtractKernelAssetsCalls, HasLen, 1) 178 c.Assert(bloader.ExtractKernelAssetsCalls[0].InstanceName(), Equals, "kernel") 179 minInfo := snap.MinimalPlaceInfo("kernel", snap.R(140)) 180 181 // undo deletes the kernel assets again 182 err = s.be.UndoSetupSnap(minInfo, "kernel", progress.Null) 183 c.Assert(err, IsNil) 184 c.Assert(bloader.RemoveKernelAssetsCalls, HasLen, 1) 185 c.Assert(bloader.RemoveKernelAssetsCalls[0].InstanceName(), Equals, "kernel") 186 } 187 188 func (s *setupSuite) TestSetupDoIdempotent(c *C) { 189 // make sure that a retry wouldn't stumble on partial work 190 // use a kernel because that does and need to do strictly more 191 192 // this cannot check systemd own behavior though around mounts! 193 194 // kernel snaps only happen on non-classic 195 defer release.MockOnClassic(false)() 196 bloader := bootloadertest.Mock("mock", c.MkDir()) 197 bootloader.Force(bloader) 198 // we don't get real mounting 199 os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") 200 defer os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") 201 202 testFiles := [][]string{ 203 {"kernel.img", "kernel"}, 204 {"initrd.img", "initrd"}, 205 {"modules/4.4.0-14-generic/foo.ko", "a module"}, 206 {"firmware/bar.bin", "some firmware"}, 207 {"meta/kernel.yaml", "version: 4.2"}, 208 } 209 snapPath := snaptest.MakeTestSnapWithFiles(c, `name: kernel 210 version: 1.0 211 type: kernel 212 `, testFiles) 213 214 si := snap.SideInfo{ 215 RealName: "kernel", 216 Revision: snap.R(140), 217 } 218 219 _, err := s.be.SetupSnap(snapPath, "kernel", &si, progress.Null) 220 c.Assert(err, IsNil) 221 c.Assert(bloader.ExtractKernelAssetsCalls, HasLen, 1) 222 c.Assert(bloader.ExtractKernelAssetsCalls[0].InstanceName(), Equals, "kernel") 223 224 // retry run 225 _, err = s.be.SetupSnap(snapPath, "kernel", &si, progress.Null) 226 c.Assert(err, IsNil) 227 c.Assert(bloader.ExtractKernelAssetsCalls, HasLen, 2) 228 c.Assert(bloader.ExtractKernelAssetsCalls[1].InstanceName(), Equals, "kernel") 229 minInfo := snap.MinimalPlaceInfo("kernel", snap.R(140)) 230 231 // sanity checks 232 l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) 233 c.Assert(l, HasLen, 1) 234 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, true) 235 236 c.Assert(osutil.FileExists(minInfo.MountFile()), Equals, true) 237 } 238 239 func (s *setupSuite) TestSetupUndoIdempotent(c *C) { 240 // make sure that a retry wouldn't stumble on partial work 241 // use a kernel because that does and need to do strictly more 242 243 // this cannot check systemd own behavior though around mounts! 244 245 // kernel snaps only happen on non-classic 246 defer release.MockOnClassic(false)() 247 bloader := bootloadertest.Mock("mock", c.MkDir()) 248 bootloader.Force(bloader) 249 // we don't get real mounting 250 os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") 251 defer os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") 252 253 testFiles := [][]string{ 254 {"kernel.img", "kernel"}, 255 {"initrd.img", "initrd"}, 256 {"modules/4.4.0-14-generic/foo.ko", "a module"}, 257 {"firmware/bar.bin", "some firmware"}, 258 {"meta/kernel.yaml", "version: 4.2"}, 259 } 260 snapPath := snaptest.MakeTestSnapWithFiles(c, `name: kernel 261 version: 1.0 262 type: kernel 263 `, testFiles) 264 265 si := snap.SideInfo{ 266 RealName: "kernel", 267 Revision: snap.R(140), 268 } 269 270 _, err := s.be.SetupSnap(snapPath, "kernel", &si, progress.Null) 271 c.Assert(err, IsNil) 272 273 minInfo := snap.MinimalPlaceInfo("kernel", snap.R(140)) 274 275 err = s.be.UndoSetupSnap(minInfo, "kernel", progress.Null) 276 c.Assert(err, IsNil) 277 278 // retry run 279 err = s.be.UndoSetupSnap(minInfo, "kernel", progress.Null) 280 c.Assert(err, IsNil) 281 282 // sanity checks 283 l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) 284 c.Assert(l, HasLen, 0) 285 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, false) 286 287 c.Assert(osutil.FileExists(minInfo.MountFile()), Equals, false) 288 289 // assets got extracted and then removed again 290 c.Assert(bloader.ExtractKernelAssetsCalls, HasLen, 1) 291 c.Assert(bloader.RemoveKernelAssetsCalls, HasLen, 1) 292 } 293 294 func (s *setupSuite) TestSetupCleanupAfterFail(c *C) { 295 snapPath := makeTestSnap(c, helloYaml1) 296 297 si := snap.SideInfo{ 298 RealName: "hello", 299 Revision: snap.R(14), 300 } 301 302 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 303 // mount unit start fails 304 if len(cmd) >= 2 && cmd[0] == "start" && strings.HasSuffix(cmd[1], ".mount") { 305 return nil, fmt.Errorf("failed") 306 } 307 return []byte("ActiveState=inactive\n"), nil 308 }) 309 defer r() 310 311 _, err := s.be.SetupSnap(snapPath, "hello", &si, progress.Null) 312 c.Assert(err, ErrorMatches, "failed") 313 314 // everything is gone 315 l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) 316 c.Check(l, HasLen, 0) 317 318 minInfo := snap.MinimalPlaceInfo("hello", snap.R(14)) 319 c.Check(osutil.FileExists(minInfo.MountDir()), Equals, false) 320 c.Check(osutil.FileExists(minInfo.MountFile()), Equals, false) 321 c.Check(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "hello_14.snap")), Equals, false) 322 } 323 324 func (s *setupSuite) TestRemoveSnapFilesDir(c *C) { 325 snapPath := makeTestSnap(c, helloYaml1) 326 327 si := snap.SideInfo{ 328 RealName: "hello", 329 Revision: snap.R(14), 330 } 331 332 snapType, err := s.be.SetupSnap(snapPath, "hello_instance", &si, progress.Null) 333 c.Assert(err, IsNil) 334 c.Check(snapType, Equals, snap.TypeApp) 335 336 minInfo := snap.MinimalPlaceInfo("hello_instance", snap.R(14)) 337 // mount dir was created 338 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, true) 339 340 s.be.RemoveSnapFiles(minInfo, snapType, progress.Null) 341 c.Assert(err, IsNil) 342 343 l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) 344 c.Assert(l, HasLen, 0) 345 c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, false) 346 c.Assert(osutil.FileExists(minInfo.MountFile()), Equals, false) 347 c.Assert(osutil.FileExists(snap.BaseDir(minInfo.InstanceName())), Equals, true) 348 c.Assert(osutil.FileExists(snap.BaseDir(minInfo.SnapName())), Equals, true) 349 350 // /snap/hello is kept as other instances exist 351 err = s.be.RemoveSnapDir(minInfo, true) 352 c.Assert(err, IsNil) 353 c.Assert(osutil.FileExists(snap.BaseDir(minInfo.InstanceName())), Equals, false) 354 c.Assert(osutil.FileExists(snap.BaseDir(minInfo.SnapName())), Equals, true) 355 356 // /snap/hello is removed when there are no more instances 357 err = s.be.RemoveSnapDir(minInfo, false) 358 c.Assert(err, IsNil) 359 c.Assert(osutil.FileExists(snap.BaseDir(minInfo.SnapName())), Equals, false) 360 }