github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/snap-preseed/main_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019-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 "io/ioutil" 25 "os" 26 "path/filepath" 27 "strings" 28 29 "testing" 30 31 "github.com/jessevdk/go-flags" 32 . "gopkg.in/check.v1" 33 34 "github.com/snapcore/snapd/asserts" 35 "github.com/snapcore/snapd/asserts/assertstest" 36 "github.com/snapcore/snapd/cmd/snap-preseed" 37 "github.com/snapcore/snapd/dirs" 38 "github.com/snapcore/snapd/osutil" 39 "github.com/snapcore/snapd/osutil/squashfs" 40 apparmor_sandbox "github.com/snapcore/snapd/sandbox/apparmor" 41 "github.com/snapcore/snapd/seed" 42 "github.com/snapcore/snapd/snap" 43 "github.com/snapcore/snapd/testutil" 44 "github.com/snapcore/snapd/timings" 45 ) 46 47 func Test(t *testing.T) { TestingT(t) } 48 49 var _ = Suite(&startPreseedSuite{}) 50 51 type startPreseedSuite struct { 52 testutil.BaseTest 53 } 54 55 func (s *startPreseedSuite) SetUpTest(c *C) { 56 s.BaseTest.SetUpTest(c) 57 restore := squashfs.MockNeedsFuse(false) 58 s.BaseTest.AddCleanup(restore) 59 } 60 61 func (s *startPreseedSuite) TearDownTest(c *C) { 62 s.BaseTest.TearDownTest(c) 63 dirs.SetRootDir("") 64 } 65 66 func testParser(c *C) *flags.Parser { 67 parser := main.Parser() 68 _, err := parser.ParseArgs([]string{}) 69 c.Assert(err, IsNil) 70 return parser 71 } 72 73 func mockVersionFiles(c *C, rootDir1, version1, rootDir2, version2 string) { 74 versions := []string{version1, version2} 75 for i, root := range []string{rootDir1, rootDir2} { 76 c.Assert(os.MkdirAll(filepath.Join(root, dirs.CoreLibExecDir), 0755), IsNil) 77 infoFile := filepath.Join(root, dirs.CoreLibExecDir, "info") 78 c.Assert(ioutil.WriteFile(infoFile, []byte(fmt.Sprintf("VERSION=%s", versions[i])), 0644), IsNil) 79 } 80 } 81 82 func mockChrootDirs(c *C, rootDir string, apparmorDir bool) func() { 83 if apparmorDir { 84 c.Assert(os.MkdirAll(filepath.Join(rootDir, "/sys/kernel/security/apparmor"), 0755), IsNil) 85 } 86 mockMountInfo := `912 920 0:57 / ${rootDir}/proc rw,nosuid,nodev,noexec,relatime - proc proc rw 87 914 913 0:7 / ${rootDir}/sys/kernel/security rw,nosuid,nodev,noexec,relatime master:8 - securityfs securityfs rw 88 915 920 0:58 / ${rootDir}/dev rw,relatime - tmpfs none rw,size=492k,mode=755,uid=100000,gid=100000 89 ` 90 return osutil.MockMountInfo(strings.Replace(mockMountInfo, "${rootDir}", rootDir, -1)) 91 } 92 93 func (s *startPreseedSuite) TestRequiresRoot(c *C) { 94 restore := main.MockOsGetuid(func() int { 95 return 1000 96 }) 97 defer restore() 98 99 parser := testParser(c) 100 c.Check(main.Run(parser, []string{"/"}), ErrorMatches, `must be run as root`) 101 } 102 103 func (s *startPreseedSuite) TestMissingArg(c *C) { 104 restore := main.MockOsGetuid(func() int { 105 return 0 106 }) 107 defer restore() 108 109 parser := testParser(c) 110 c.Check(main.Run(parser, nil), ErrorMatches, `need chroot path as argument`) 111 } 112 113 func (s *startPreseedSuite) TestChrootDoesntExist(c *C) { 114 restore := main.MockOsGetuid(func() int { return 0 }) 115 defer restore() 116 117 parser := testParser(c) 118 c.Check(main.Run(parser, []string{"/non-existing-dir"}), ErrorMatches, `cannot verify "/non-existing-dir": is not a directory`) 119 } 120 121 func (s *startPreseedSuite) TestChrootValidationUnhappy(c *C) { 122 restore := main.MockOsGetuid(func() int { return 0 }) 123 defer restore() 124 125 tmpDir := c.MkDir() 126 defer osutil.MockMountInfo("")() 127 128 parser := testParser(c) 129 c.Check(main.Run(parser, []string{tmpDir}), ErrorMatches, "cannot preseed without the following mountpoints:\n - .*/dev\n - .*/proc\n - .*/sys/kernel/security") 130 } 131 132 func (s *startPreseedSuite) TestChrootValidationUnhappyNoApparmor(c *C) { 133 restore := main.MockOsGetuid(func() int { return 0 }) 134 defer restore() 135 136 tmpDir := c.MkDir() 137 defer mockChrootDirs(c, tmpDir, false)() 138 139 parser := testParser(c) 140 c.Check(main.Run(parser, []string{tmpDir}), ErrorMatches, `cannot preseed without access to ".*sys/kernel/security/apparmor"`) 141 } 142 143 func (s *startPreseedSuite) TestChrootValidationAlreadyPreseeded(c *C) { 144 restore := main.MockOsGetuid(func() int { return 0 }) 145 defer restore() 146 147 tmpDir := c.MkDir() 148 snapdDir := filepath.Dir(dirs.SnapStateFile) 149 c.Assert(os.MkdirAll(filepath.Join(tmpDir, snapdDir), 0755), IsNil) 150 c.Assert(ioutil.WriteFile(filepath.Join(tmpDir, dirs.SnapStateFile), nil, os.ModePerm), IsNil) 151 152 parser := testParser(c) 153 c.Check(main.Run(parser, []string{tmpDir}), ErrorMatches, fmt.Sprintf("the system at %q appears to be preseeded, pass --reset flag to clean it up", tmpDir)) 154 } 155 156 func (s *startPreseedSuite) TestChrootFailure(c *C) { 157 restoreOsGuid := main.MockOsGetuid(func() int { return 0 }) 158 defer restoreOsGuid() 159 160 restoreSyscallChroot := main.MockSyscallChroot(func(path string) error { 161 return fmt.Errorf("FAIL: %s", path) 162 }) 163 defer restoreSyscallChroot() 164 165 tmpDir := c.MkDir() 166 defer mockChrootDirs(c, tmpDir, true)() 167 168 parser := testParser(c) 169 c.Check(main.Run(parser, []string{tmpDir}), ErrorMatches, fmt.Sprintf("cannot chroot into %s: FAIL: %s", tmpDir, tmpDir)) 170 } 171 172 func (s *startPreseedSuite) TestRunPreseedHappy(c *C) { 173 tmpDir := c.MkDir() 174 dirs.SetRootDir(tmpDir) 175 defer mockChrootDirs(c, tmpDir, true)() 176 177 restoreOsGuid := main.MockOsGetuid(func() int { return 0 }) 178 defer restoreOsGuid() 179 180 restoreSyscallChroot := main.MockSyscallChroot(func(path string) error { return nil }) 181 defer restoreSyscallChroot() 182 183 mockMountCmd := testutil.MockCommand(c, "mount", "") 184 defer mockMountCmd.Restore() 185 186 mockUmountCmd := testutil.MockCommand(c, "umount", "") 187 defer mockUmountCmd.Restore() 188 189 targetSnapdRoot := filepath.Join(tmpDir, "target-core-mounted-here") 190 restoreMountPath := main.MockSnapdMountPath(targetSnapdRoot) 191 defer restoreMountPath() 192 193 restoreSystemSnapFromSeed := main.MockSystemSnapFromSeed(func(string) (string, error) { return "/a/core.snap", nil }) 194 defer restoreSystemSnapFromSeed() 195 196 mockTargetSnapd := testutil.MockCommand(c, filepath.Join(targetSnapdRoot, "usr/lib/snapd/snapd"), `#!/bin/sh 197 if [ "$SNAPD_PRESEED" != "1" ]; then 198 exit 1 199 fi 200 `) 201 defer mockTargetSnapd.Restore() 202 203 mockSnapdFromDeb := testutil.MockCommand(c, filepath.Join(tmpDir, "usr/lib/snapd/snapd"), `#!/bin/sh 204 exit 1 205 `) 206 defer mockSnapdFromDeb.Restore() 207 208 // snapd from the snap is newer than deb 209 mockVersionFiles(c, targetSnapdRoot, "2.44.0", tmpDir, "2.41.0") 210 211 parser := testParser(c) 212 c.Check(main.Run(parser, []string{tmpDir}), IsNil) 213 214 c.Assert(mockMountCmd.Calls(), HasLen, 1) 215 // note, tmpDir, targetSnapdRoot are contactenated again cause we're not really chrooting in the test 216 // and mocking dirs.RootDir 217 c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "squashfs", "-o", "ro,x-gdu.hide", "/a/core.snap", filepath.Join(tmpDir, targetSnapdRoot)}) 218 219 c.Assert(mockTargetSnapd.Calls(), HasLen, 1) 220 c.Check(mockTargetSnapd.Calls()[0], DeepEquals, []string{"snapd"}) 221 222 c.Assert(mockSnapdFromDeb.Calls(), HasLen, 0) 223 224 // relative chroot path works too 225 tmpDirPath, relativeChroot := filepath.Split(tmpDir) 226 pwd, err := os.Getwd() 227 c.Assert(err, IsNil) 228 defer func() { 229 os.Chdir(pwd) 230 }() 231 c.Assert(os.Chdir(tmpDirPath), IsNil) 232 c.Check(main.Run(parser, []string{relativeChroot}), IsNil) 233 } 234 235 func (s *startPreseedSuite) TestRunPreseedHappyDebVersionIsNewer(c *C) { 236 tmpDir := c.MkDir() 237 dirs.SetRootDir(tmpDir) 238 defer mockChrootDirs(c, tmpDir, true)() 239 240 restoreOsGuid := main.MockOsGetuid(func() int { return 0 }) 241 defer restoreOsGuid() 242 243 restoreSyscallChroot := main.MockSyscallChroot(func(path string) error { return nil }) 244 defer restoreSyscallChroot() 245 246 mockMountCmd := testutil.MockCommand(c, "mount", "") 247 defer mockMountCmd.Restore() 248 249 mockUmountCmd := testutil.MockCommand(c, "umount", "") 250 defer mockUmountCmd.Restore() 251 252 targetSnapdRoot := filepath.Join(tmpDir, "target-core-mounted-here") 253 restoreMountPath := main.MockSnapdMountPath(targetSnapdRoot) 254 defer restoreMountPath() 255 256 restoreSystemSnapFromSeed := main.MockSystemSnapFromSeed(func(string) (string, error) { return "/a/core.snap", nil }) 257 defer restoreSystemSnapFromSeed() 258 259 c.Assert(os.MkdirAll(filepath.Join(targetSnapdRoot, "usr/lib/snapd/"), 0755), IsNil) 260 mockSnapdFromSnap := testutil.MockCommand(c, filepath.Join(targetSnapdRoot, "usr/lib/snapd/snapd"), `#!/bin/sh 261 exit 1 262 `) 263 defer mockSnapdFromSnap.Restore() 264 265 mockSnapdFromDeb := testutil.MockCommand(c, filepath.Join(tmpDir, "usr/lib/snapd/snapd"), `#!/bin/sh 266 if [ "$SNAPD_PRESEED" != "1" ]; then 267 exit 1 268 fi 269 `) 270 defer mockSnapdFromDeb.Restore() 271 272 // snapd from the deb is newer than snap 273 mockVersionFiles(c, targetSnapdRoot, "2.44.0", tmpDir, "2.45.0") 274 275 parser := testParser(c) 276 c.Check(main.Run(parser, []string{tmpDir}), IsNil) 277 278 c.Assert(mockMountCmd.Calls(), HasLen, 1) 279 // note, tmpDir, targetSnapdRoot are contactenated again cause we're not really chrooting in the test 280 // and mocking dirs.RootDir 281 c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "squashfs", "-o", "ro,x-gdu.hide", "/a/core.snap", filepath.Join(tmpDir, targetSnapdRoot)}) 282 283 c.Assert(mockSnapdFromDeb.Calls(), HasLen, 1) 284 c.Check(mockSnapdFromDeb.Calls()[0], DeepEquals, []string{"snapd"}) 285 c.Assert(mockSnapdFromSnap.Calls(), HasLen, 0) 286 } 287 288 type Fake16Seed struct { 289 AssertsModel *asserts.Model 290 Essential []*seed.Snap 291 LoadMetaErr error 292 LoadAssertionsErr error 293 UsesSnapd bool 294 } 295 296 // Fake implementation of seed.Seed interface 297 298 func mockClassicModel() *asserts.Model { 299 headers := map[string]interface{}{ 300 "type": "model", 301 "authority-id": "brand", 302 "series": "16", 303 "brand-id": "brand", 304 "model": "classicbaz-3000", 305 "classic": "true", 306 "timestamp": "2018-01-01T08:00:00+00:00", 307 } 308 return assertstest.FakeAssertion(headers, nil).(*asserts.Model) 309 } 310 311 func (fs *Fake16Seed) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error { 312 return fs.LoadAssertionsErr 313 } 314 315 func (fs *Fake16Seed) Model() *asserts.Model { 316 return fs.AssertsModel 317 } 318 319 func (fs *Fake16Seed) Brand() (*asserts.Account, error) { 320 headers := map[string]interface{}{ 321 "type": "account", 322 "account-id": "brand", 323 "display-name": "fake brand", 324 "username": "brand", 325 "timestamp": "2018-01-01T08:00:00+00:00", 326 } 327 return assertstest.FakeAssertion(headers, nil).(*asserts.Account), nil 328 } 329 330 func (fs *Fake16Seed) LoadMeta(tm timings.Measurer) error { 331 return fs.LoadMetaErr 332 } 333 334 func (fs *Fake16Seed) UsesSnapdSnap() bool { 335 return fs.UsesSnapd 336 } 337 338 func (fs *Fake16Seed) EssentialSnaps() []*seed.Snap { 339 return fs.Essential 340 } 341 342 func (fs *Fake16Seed) ModeSnaps(mode string) ([]*seed.Snap, error) { 343 return nil, nil 344 } 345 346 func (s *startPreseedSuite) TestSystemSnapFromSeed(c *C) { 347 tmpDir := c.MkDir() 348 349 restore := main.MockSeedOpen(func(rootDir, label string) (seed.Seed, error) { 350 return &Fake16Seed{ 351 AssertsModel: mockClassicModel(), 352 Essential: []*seed.Snap{{Path: "/some/path/core", SideInfo: &snap.SideInfo{RealName: "core"}}}, 353 }, nil 354 }) 355 defer restore() 356 357 path, err := main.SystemSnapFromSeed(tmpDir) 358 c.Assert(err, IsNil) 359 c.Check(path, Equals, "/some/path/core") 360 } 361 362 func (s *startPreseedSuite) TestSystemSnapFromSnapdSeed(c *C) { 363 tmpDir := c.MkDir() 364 365 restore := main.MockSeedOpen(func(rootDir, label string) (seed.Seed, error) { 366 return &Fake16Seed{ 367 AssertsModel: mockClassicModel(), 368 Essential: []*seed.Snap{{Path: "/some/path/snapd.snap", SideInfo: &snap.SideInfo{RealName: "snapd"}}}, 369 UsesSnapd: true, 370 }, nil 371 }) 372 defer restore() 373 374 path, err := main.SystemSnapFromSeed(tmpDir) 375 c.Assert(err, IsNil) 376 c.Check(path, Equals, "/some/path/snapd.snap") 377 } 378 379 func (s *startPreseedSuite) TestSystemSnapFromSeedOpenError(c *C) { 380 tmpDir := c.MkDir() 381 382 restore := main.MockSeedOpen(func(rootDir, label string) (seed.Seed, error) { return nil, fmt.Errorf("fail") }) 383 defer restore() 384 385 _, err := main.SystemSnapFromSeed(tmpDir) 386 c.Assert(err, ErrorMatches, "fail") 387 } 388 389 func (s *startPreseedSuite) TestSystemSnapFromSeedErrors(c *C) { 390 tmpDir := c.MkDir() 391 392 fakeSeed := &Fake16Seed{} 393 fakeSeed.AssertsModel = mockClassicModel() 394 395 restore := main.MockSeedOpen(func(rootDir, label string) (seed.Seed, error) { return fakeSeed, nil }) 396 defer restore() 397 398 fakeSeed.Essential = []*seed.Snap{{Path: "", SideInfo: &snap.SideInfo{RealName: "core"}}} 399 _, err := main.SystemSnapFromSeed(tmpDir) 400 c.Assert(err, ErrorMatches, "core snap not found") 401 402 fakeSeed.Essential = []*seed.Snap{{Path: "/some/path", SideInfo: &snap.SideInfo{RealName: "foosnap"}}} 403 _, err = main.SystemSnapFromSeed(tmpDir) 404 c.Assert(err, ErrorMatches, "core snap not found") 405 406 fakeSeed.LoadMetaErr = fmt.Errorf("load meta failed") 407 _, err = main.SystemSnapFromSeed(tmpDir) 408 c.Assert(err, ErrorMatches, "load meta failed") 409 410 fakeSeed.LoadMetaErr = nil 411 fakeSeed.LoadAssertionsErr = fmt.Errorf("load assertions failed") 412 _, err = main.SystemSnapFromSeed(tmpDir) 413 c.Assert(err, ErrorMatches, "load assertions failed") 414 } 415 416 func (s *startPreseedSuite) TestClassicRequired(c *C) { 417 tmpDir := c.MkDir() 418 419 headers := map[string]interface{}{ 420 "type": "model", 421 "authority-id": "brand", 422 "series": "16", 423 "brand-id": "brand", 424 "model": "baz-3000", 425 "architecture": "armhf", 426 "gadget": "brand-gadget", 427 "kernel": "kernel", 428 "timestamp": "2018-01-01T08:00:00+00:00", 429 } 430 431 fakeSeed := &Fake16Seed{} 432 fakeSeed.AssertsModel = assertstest.FakeAssertion(headers, nil).(*asserts.Model) 433 434 restore := main.MockSeedOpen(func(rootDir, label string) (seed.Seed, error) { return fakeSeed, nil }) 435 defer restore() 436 437 _, err := main.SystemSnapFromSeed(tmpDir) 438 c.Assert(err, ErrorMatches, "preseeding is only supported on classic systems") 439 } 440 441 func (s *startPreseedSuite) TestRunPreseedUnsupportedVersion(c *C) { 442 tmpDir := c.MkDir() 443 dirs.SetRootDir(tmpDir) 444 c.Assert(os.MkdirAll(filepath.Join(tmpDir, "usr/lib/snapd/"), 0755), IsNil) 445 defer mockChrootDirs(c, tmpDir, true)() 446 447 restoreOsGuid := main.MockOsGetuid(func() int { return 0 }) 448 defer restoreOsGuid() 449 450 restoreSyscallChroot := main.MockSyscallChroot(func(path string) error { return nil }) 451 defer restoreSyscallChroot() 452 453 mockMountCmd := testutil.MockCommand(c, "mount", "") 454 defer mockMountCmd.Restore() 455 456 targetSnapdRoot := filepath.Join(tmpDir, "target-core-mounted-here") 457 restoreMountPath := main.MockSnapdMountPath(targetSnapdRoot) 458 defer restoreMountPath() 459 460 restoreSystemSnapFromSeed := main.MockSystemSnapFromSeed(func(string) (string, error) { return "/a/core.snap", nil }) 461 defer restoreSystemSnapFromSeed() 462 463 c.Assert(os.MkdirAll(filepath.Join(targetSnapdRoot, "usr/lib/snapd/"), 0755), IsNil) 464 mockTargetSnapd := testutil.MockCommand(c, filepath.Join(targetSnapdRoot, "usr/lib/snapd/snapd"), "") 465 defer mockTargetSnapd.Restore() 466 467 infoFile := filepath.Join(targetSnapdRoot, dirs.CoreLibExecDir, "info") 468 c.Assert(ioutil.WriteFile(infoFile, []byte("VERSION=2.43.0"), 0644), IsNil) 469 470 // simulate snapd version from the deb 471 infoFile = filepath.Join(filepath.Join(tmpDir, dirs.CoreLibExecDir, "info")) 472 c.Assert(ioutil.WriteFile(infoFile, []byte("VERSION=2.41.0"), 0644), IsNil) 473 474 parser := testParser(c) 475 c.Check(main.Run(parser, []string{tmpDir}), ErrorMatches, 476 `snapd 2.43.0 from the target system does not support preseeding, the minimum required version is 2.43.3\+`) 477 } 478 479 func (s *startPreseedSuite) TestChooseTargetSnapdVersion(c *C) { 480 tmpDir := c.MkDir() 481 dirs.SetRootDir(tmpDir) 482 c.Assert(os.MkdirAll(filepath.Join(tmpDir, "usr/lib/snapd/"), 0755), IsNil) 483 484 targetSnapdRoot := filepath.Join(tmpDir, "target-core-mounted-here") 485 c.Assert(os.MkdirAll(filepath.Join(targetSnapdRoot, "usr/lib/snapd/"), 0755), IsNil) 486 restoreMountPath := main.MockSnapdMountPath(targetSnapdRoot) 487 defer restoreMountPath() 488 489 var versions = []struct { 490 fromSnap string 491 fromDeb string 492 expectedPath string 493 expectedVersion string 494 expectedErr string 495 }{ 496 { 497 fromDeb: "2.44.0", 498 fromSnap: "2.45.3+git123", 499 // snap version wins 500 expectedVersion: "2.45.3+git123", 501 expectedPath: filepath.Join(tmpDir, "target-core-mounted-here/usr/lib/snapd/snapd"), 502 }, 503 { 504 fromDeb: "2.44.0", 505 fromSnap: "2.44.0", 506 // snap version wins 507 expectedVersion: "2.44.0", 508 expectedPath: filepath.Join(tmpDir, "target-core-mounted-here/usr/lib/snapd/snapd"), 509 }, 510 { 511 fromDeb: "2.45.1+20.04", 512 fromSnap: "2.45.1", 513 // deb version wins 514 expectedVersion: "2.45.1+20.04", 515 expectedPath: filepath.Join(tmpDir, "usr/lib/snapd/snapd"), 516 }, 517 { 518 fromDeb: "2.45.2", 519 fromSnap: "2.45.1", 520 // deb version wins 521 expectedVersion: "2.45.2", 522 expectedPath: filepath.Join(tmpDir, "usr/lib/snapd/snapd"), 523 }, 524 { 525 fromSnap: "2.45.1", 526 expectedErr: fmt.Sprintf("cannot open snapd info file %q.*", filepath.Join(tmpDir, "usr/lib/snapd/info")), 527 }, 528 { 529 fromDeb: "2.45.1", 530 expectedErr: fmt.Sprintf("cannot open snapd info file %q.*", filepath.Join(tmpDir, "target-core-mounted-here/usr/lib/snapd/info")), 531 }, 532 } 533 534 for _, test := range versions { 535 infoFile := filepath.Join(tmpDir, "usr/lib/snapd/info") 536 os.Remove(infoFile) 537 if test.fromDeb != "" { 538 c.Assert(ioutil.WriteFile(infoFile, []byte(fmt.Sprintf("VERSION=%s", test.fromDeb)), 0644), IsNil) 539 } 540 infoFile = filepath.Join(targetSnapdRoot, "usr/lib/snapd/info") 541 os.Remove(infoFile) 542 if test.fromSnap != "" { 543 c.Assert(ioutil.WriteFile(infoFile, []byte(fmt.Sprintf("VERSION=%s", test.fromSnap)), 0644), IsNil) 544 } 545 546 targetSnapd, err := main.ChooseTargetSnapdVersion() 547 if test.expectedErr != "" { 548 c.Assert(err, ErrorMatches, test.expectedErr) 549 } else { 550 c.Assert(err, IsNil) 551 c.Assert(targetSnapd, NotNil) 552 path, version := main.SnapdPathAndVersion(targetSnapd) 553 c.Check(path, Equals, test.expectedPath) 554 c.Check(version, Equals, test.expectedVersion) 555 } 556 } 557 } 558 559 func (s *startPreseedSuite) TestRunPreseedAgainstFilesystemRoot(c *C) { 560 restore := main.MockOsGetuid(func() int { return 0 }) 561 defer restore() 562 563 parser := testParser(c) 564 c.Assert(main.Run(parser, []string{"/"}), ErrorMatches, `cannot run snap-preseed against /`) 565 } 566 567 func (s *startPreseedSuite) TestReset(c *C) { 568 restore := main.MockOsGetuid(func() int { return 0 }) 569 defer restore() 570 571 startDir, err := os.Getwd() 572 c.Assert(err, IsNil) 573 defer func() { 574 os.Chdir(startDir) 575 }() 576 577 for _, isRelative := range []bool{false, true} { 578 tmpDir := c.MkDir() 579 resetDirArg := tmpDir 580 if isRelative { 581 var parentDir string 582 parentDir, resetDirArg = filepath.Split(tmpDir) 583 os.Chdir(parentDir) 584 } 585 586 // mock some preseeding artifacts 587 artifacts := []struct { 588 path string 589 // if symlinkTarget is not empty, then a path -> symlinkTarget symlink 590 // will be created instead of a regular file. 591 symlinkTarget string 592 }{ 593 {dirs.SnapStateFile, ""}, 594 {dirs.SnapSystemKeyFile, ""}, 595 {filepath.Join(dirs.SnapDesktopFilesDir, "foo.desktop"), ""}, 596 {filepath.Join(dirs.SnapDesktopIconsDir, "foo.png"), ""}, 597 {filepath.Join(dirs.SnapMountPolicyDir, "foo.fstab"), ""}, 598 {filepath.Join(dirs.SnapBlobDir, "foo.snap"), ""}, 599 {filepath.Join(dirs.SnapUdevRulesDir, "foo-snap.bar.rules"), ""}, 600 {filepath.Join(dirs.SnapDBusSystemPolicyDir, "snap.foo.bar.conf"), ""}, 601 {filepath.Join(dirs.SnapServicesDir, "snap.foo.service"), ""}, 602 {filepath.Join(dirs.SnapServicesDir, "snap.foo.timer"), ""}, 603 {filepath.Join(dirs.SnapServicesDir, "snap.foo.socket"), ""}, 604 {filepath.Join(dirs.SnapServicesDir, "snap-foo.mount"), ""}, 605 {filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", "snap-foo.mount"), ""}, 606 {filepath.Join(dirs.SnapDataDir, "foo", "bar"), ""}, 607 {filepath.Join(dirs.SnapCacheDir, "foocache", "bar"), ""}, 608 {filepath.Join(apparmor_sandbox.CacheDir, "foo", "bar"), ""}, 609 {filepath.Join(dirs.SnapAppArmorDir, "foo"), ""}, 610 {filepath.Join(dirs.SnapAssertsDBDir, "foo"), ""}, 611 {filepath.Join(dirs.FeaturesDir, "foo"), ""}, 612 {filepath.Join(dirs.SnapDeviceDir, "foo-1", "bar"), ""}, 613 {filepath.Join(dirs.SnapCookieDir, "foo"), ""}, 614 {filepath.Join(dirs.SnapSeqDir, "foo.json"), ""}, 615 {filepath.Join(dirs.SnapMountDir, "foo", "bin"), ""}, 616 {filepath.Join(dirs.SnapSeccompDir, "foo.bin"), ""}, 617 // bash-completion symlinks 618 {filepath.Join(dirs.CompletersDir, "foo.bar"), "/a/snapd/complete.sh"}, 619 {filepath.Join(dirs.CompletersDir, "foo"), "foo.bar"}, 620 } 621 622 for _, art := range artifacts { 623 fullPath := filepath.Join(tmpDir, art.path) 624 // create parent dir 625 c.Assert(os.MkdirAll(filepath.Dir(fullPath), 0755), IsNil) 626 if art.symlinkTarget != "" { 627 // note, symlinkTarget is not relative to tmpDir 628 c.Assert(os.Symlink(art.symlinkTarget, fullPath), IsNil) 629 } else { 630 c.Assert(ioutil.WriteFile(fullPath, nil, os.ModePerm), IsNil) 631 } 632 } 633 634 checkArtifacts := func(exists bool) { 635 for _, art := range artifacts { 636 fullPath := filepath.Join(tmpDir, art.path) 637 if art.symlinkTarget != "" { 638 c.Check(osutil.IsSymlink(fullPath), Equals, exists, Commentf("offending symlink: %s", fullPath)) 639 } else { 640 c.Check(osutil.FileExists(fullPath), Equals, exists, Commentf("offending file: %s", fullPath)) 641 } 642 } 643 } 644 645 // sanity 646 checkArtifacts(true) 647 648 snapdDir := filepath.Dir(dirs.SnapStateFile) 649 c.Assert(os.MkdirAll(filepath.Join(tmpDir, snapdDir), 0755), IsNil) 650 c.Assert(ioutil.WriteFile(filepath.Join(tmpDir, dirs.SnapStateFile), nil, os.ModePerm), IsNil) 651 652 parser := testParser(c) 653 c.Assert(main.Run(parser, []string{"--reset", resetDirArg}), IsNil) 654 655 checkArtifacts(false) 656 657 // running reset again is ok 658 parser = testParser(c) 659 c.Assert(main.Run(parser, []string{"--reset", resetDirArg}), IsNil) 660 661 // reset complains if target directory doesn't exist 662 c.Assert(main.Run(parser, []string{"--reset", "/non/existing/chrootpath"}), ErrorMatches, `cannot reset non-existing directory "/non/existing/chrootpath"`) 663 664 // reset complains if target is not a directory 665 dummyFile := filepath.Join(resetDirArg, "foo") 666 c.Assert(ioutil.WriteFile(dummyFile, nil, os.ModePerm), IsNil) 667 err = main.Run(parser, []string{"--reset", dummyFile}) 668 // the error message is always with an absolute file, so make the path 669 // absolute if we are running the relative test to properly match 670 if isRelative { 671 var err2 error 672 dummyFile, err2 = filepath.Abs(dummyFile) 673 c.Assert(err2, IsNil) 674 } 675 c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot reset %q, it is not a directory`, dummyFile)) 676 } 677 678 }