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  }