github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/sandbox/cgroup/freezer_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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 cgroup_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"sort"
    28  	"strings"
    29  
    30  	. "gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/dirs"
    33  	"github.com/snapcore/snapd/sandbox/cgroup"
    34  	"github.com/snapcore/snapd/testutil"
    35  )
    36  
    37  type freezerV1Suite struct{}
    38  
    39  var _ = Suite(&freezerV1Suite{})
    40  
    41  func (s *freezerV1Suite) TestFreezeSnapProcessesV1(c *C) {
    42  	defer cgroup.MockVersion(cgroup.V1, nil)()
    43  	dirs.SetRootDir(c.MkDir())
    44  	defer dirs.SetRootDir("")
    45  
    46  	n := "foo"                                                                 // snap name
    47  	p := filepath.Join(cgroup.FreezerCgroupV1Dir(), fmt.Sprintf("snap.%s", n)) // snap freezer cgroup
    48  	f := filepath.Join(p, "freezer.state")                                     // freezer.state file of the cgroup
    49  
    50  	// When the freezer cgroup filesystem doesn't exist we do nothing at all.
    51  	c.Assert(cgroup.FreezeSnapProcesses(n), IsNil)
    52  	_, err := os.Stat(f)
    53  	c.Assert(os.IsNotExist(err), Equals, true)
    54  
    55  	// When the freezer cgroup filesystem exists but the particular cgroup
    56  	// doesn't exist we don nothing at all.
    57  	c.Assert(os.MkdirAll(cgroup.FreezerCgroupV1Dir(), 0755), IsNil)
    58  	c.Assert(cgroup.FreezeSnapProcesses(n), IsNil)
    59  	_, err = os.Stat(f)
    60  	c.Assert(os.IsNotExist(err), Equals, true)
    61  
    62  	// When the cgroup exists we write FROZEN the freezer.state file.
    63  	c.Assert(os.MkdirAll(p, 0755), IsNil)
    64  	c.Assert(cgroup.FreezeSnapProcesses(n), IsNil)
    65  	_, err = os.Stat(f)
    66  	c.Assert(err, IsNil)
    67  	c.Assert(f, testutil.FileEquals, `FROZEN`)
    68  }
    69  
    70  func (s *freezerV1Suite) TestThawSnapProcessesV1(c *C) {
    71  	defer cgroup.MockVersion(cgroup.V1, nil)()
    72  	dirs.SetRootDir(c.MkDir())
    73  	defer dirs.SetRootDir("")
    74  
    75  	n := "foo"                                                                 // snap name
    76  	p := filepath.Join(cgroup.FreezerCgroupV1Dir(), fmt.Sprintf("snap.%s", n)) // snap freezer cgroup
    77  	f := filepath.Join(p, "freezer.state")                                     // freezer.state file of the cgroup
    78  
    79  	// When the freezer cgroup filesystem doesn't exist we do nothing at all.
    80  	c.Assert(cgroup.ThawSnapProcesses(n), IsNil)
    81  	_, err := os.Stat(f)
    82  	c.Assert(os.IsNotExist(err), Equals, true)
    83  
    84  	// When the freezer cgroup filesystem exists but the particular cgroup
    85  	// doesn't exist we don nothing at all.
    86  	c.Assert(os.MkdirAll(cgroup.FreezerCgroupV1Dir(), 0755), IsNil)
    87  	c.Assert(cgroup.ThawSnapProcesses(n), IsNil)
    88  	_, err = os.Stat(f)
    89  	c.Assert(os.IsNotExist(err), Equals, true)
    90  
    91  	// When the cgroup exists we write THAWED the freezer.state file.
    92  	c.Assert(os.MkdirAll(p, 0755), IsNil)
    93  	c.Assert(cgroup.ThawSnapProcesses(n), IsNil)
    94  	_, err = os.Stat(f)
    95  	c.Assert(err, IsNil)
    96  	c.Assert(f, testutil.FileEquals, `THAWED`)
    97  }
    98  
    99  type freezerV2Suite struct{}
   100  
   101  var _ = Suite(&freezerV2Suite{})
   102  
   103  func (s *freezerV2Suite) TestFreezeSnapProcessesV2OtherGroups(c *C) {
   104  	defer cgroup.MockVersion(cgroup.V2, nil)()
   105  	dirs.SetRootDir(c.MkDir())
   106  	defer dirs.SetRootDir("")
   107  
   108  	// app started by root
   109  	g1 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.1234-1234-1234.scope/cgroup.freeze")
   110  	// service started by systemd
   111  	g2 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/cgroup.freeze")
   112  	// user applications
   113  	g3 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/user.slice/user-1234.slice/user@1234.service/snap.foo.user-app.1234-1234-1234.scope/cgroup.freeze")
   114  	// user service
   115  	g4 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/user.slice/user-1234.slice/user@1234.service/snap.foo.user-svc.service/cgroup.freeze")
   116  	canary := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.canary.svc.service/cgroup.freeze")
   117  	// a subgroup of the group of a snap
   118  	canarySubgroup := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/snap.foo.subgroup.scope/cgroup.freeze")
   119  
   120  	pid := os.Getpid()
   121  
   122  	// freezing needs to inspect our own cgroup, which will fail without
   123  	// proper mocking
   124  	err := cgroup.FreezeSnapProcesses("foo")
   125  	c.Check(err, ErrorMatches, fmt.Sprintf("open %s/proc/%v/cgroup: no such file or directory", dirs.GlobalRootDir, pid))
   126  
   127  	procPidCgroup := filepath.Join(dirs.GlobalRootDir, fmt.Sprintf("proc/%v/cgroup", pid))
   128  	c.Assert(os.MkdirAll(filepath.Dir(procPidCgroup), 0755), IsNil)
   129  	c.Assert(ioutil.WriteFile(procPidCgroup, []byte("0::/foo/bar"), 0755), IsNil)
   130  
   131  	// When the freezer cgroup filesystem doesn't exist we do nothing at all.
   132  	c.Assert(cgroup.FreezeSnapProcesses("foo"), IsNil)
   133  
   134  	for _, p := range []string{g1, g2, g3, g4, canary, canarySubgroup} {
   135  		_, err := os.Stat(p)
   136  		c.Assert(os.IsNotExist(err), Equals, true)
   137  	}
   138  
   139  	// prepare the stage
   140  	for _, p := range []string{g1, g2, g3, g4, canary, canarySubgroup} {
   141  		c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   142  		c.Assert(ioutil.WriteFile(p, []byte("0"), 0644), IsNil)
   143  	}
   144  
   145  	c.Assert(cgroup.FreezeSnapProcesses("foo"), IsNil)
   146  	for _, p := range []string{g1, g2, g3, g4} {
   147  		c.Check(p, testutil.FileEquals, "1")
   148  	}
   149  	// canaries have not been changed
   150  	c.Assert(canary, testutil.FileEquals, "0")
   151  	c.Assert(canarySubgroup, testutil.FileEquals, "0")
   152  
   153  	// all groups are 'frozen', repeating the action does not break anything
   154  	c.Assert(cgroup.FreezeSnapProcesses("foo"), IsNil)
   155  	for _, p := range []string{g1, g2, g3, g4} {
   156  		c.Check(p, testutil.FileEquals, "1")
   157  	}
   158  	// canaries have not been changed
   159  	c.Assert(canary, testutil.FileEquals, "0")
   160  	c.Assert(canarySubgroup, testutil.FileEquals, "0")
   161  
   162  	// unfreeze some groups
   163  	for _, p := range []string{g2, g3} {
   164  		c.Assert(ioutil.WriteFile(p, []byte("0"), 0644), IsNil)
   165  	}
   166  	c.Assert(cgroup.FreezeSnapProcesses("foo"), IsNil)
   167  	// all are frozen again
   168  	for _, p := range []string{g1, g2, g3, g4} {
   169  		c.Check(p, testutil.FileEquals, "1")
   170  	}
   171  }
   172  
   173  func (s *freezerV2Suite) TestFreezeSnapProcessesV2OwnGroup(c *C) {
   174  	defer cgroup.MockVersion(cgroup.V2, nil)()
   175  	dirs.SetRootDir(c.MkDir())
   176  	defer dirs.SetRootDir("")
   177  
   178  	// our own cgroup
   179  	gOwn := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.own-own-own.scope/cgroup.freeze")
   180  	// app started by root
   181  	g1 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.1234-1234-1234.scope/cgroup.freeze")
   182  	// service started by systemd
   183  	g2 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/cgroup.freeze")
   184  	// user applications
   185  	g3 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/user.slice/user-1234.slice/user@1234.service/snap.foo.user-app.1234-1234-1234.scope/cgroup.freeze")
   186  	// user service
   187  	g4 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/user.slice/user-1234.slice/user@1234.service/snap.foo.user-svc.service/cgroup.freeze")
   188  	canary := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.canary.svc.service/cgroup.freeze")
   189  	// a subgroup of the group of a snap
   190  
   191  	pid := os.Getpid()
   192  
   193  	// freezing needs to inspect our own cgroup, which will fail without
   194  	// proper mocking
   195  	err := cgroup.FreezeSnapProcesses("foo")
   196  	c.Check(err, ErrorMatches, fmt.Sprintf("open %s/proc/%v/cgroup: no such file or directory", dirs.GlobalRootDir, pid))
   197  
   198  	procPidCgroup := filepath.Join(dirs.GlobalRootDir, fmt.Sprintf("proc/%v/cgroup", pid))
   199  	c.Assert(os.MkdirAll(filepath.Dir(procPidCgroup), 0755), IsNil)
   200  	// mock our own group
   201  	c.Assert(ioutil.WriteFile(procPidCgroup, []byte("0::/system.slice/snap.foo.app.own-own-own.scope"), 0755), IsNil)
   202  	// prepare the stage
   203  	for _, p := range []string{gOwn, g1, g2, g3, g4, canary} {
   204  		c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   205  		c.Assert(ioutil.WriteFile(p, []byte("0"), 0644), IsNil)
   206  	}
   207  
   208  	c.Assert(cgroup.FreezeSnapProcesses("foo"), IsNil)
   209  	// our own group is not frozen
   210  	c.Assert(gOwn, testutil.FileEquals, "0")
   211  	// canaries have not been changed
   212  	c.Assert(canary, testutil.FileEquals, "0")
   213  	// other snap groups are frozen
   214  	for _, p := range []string{g1, g2, g3, g4} {
   215  		c.Check(p, testutil.FileEquals, "1")
   216  	}
   217  }
   218  
   219  func (s *freezerV2Suite) TestThawSnapProcessesV2(c *C) {
   220  	defer cgroup.MockVersion(cgroup.V2, nil)()
   221  	dirs.SetRootDir(c.MkDir())
   222  	defer dirs.SetRootDir("")
   223  
   224  	// app started by root
   225  	g1 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.1234-1234-1234.scope/cgroup.freeze")
   226  	// service started by systemd
   227  	g2 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/cgroup.freeze")
   228  	// user applications
   229  	g3 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/user.slice/user-1234.slice/user@1234.service/snap.foo.user-app.1234-1234-1234.scope/cgroup.freeze")
   230  	// user service
   231  	g4 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/user.slice/user-1234.slice/user@1234.service/snap.foo.user-svc.service/cgroup.freeze")
   232  	canary := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.canary.svc.service/cgroup.freeze")
   233  	// a subgroup of the group of a snap
   234  	canarySubgroup := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/snap.foo.subgroup.scope/cgroup.freeze")
   235  
   236  	// thawing when no groups exist does not break anything
   237  	c.Assert(cgroup.ThawSnapProcesses("foo"), IsNil)
   238  
   239  	for _, p := range []string{g1, g2, g3, g4, canary} {
   240  		_, err := os.Stat(p)
   241  		c.Assert(os.IsNotExist(err), Equals, true)
   242  	}
   243  
   244  	// prepare the stage
   245  	for _, p := range []string{g1, g2, g3, g4, canary, canarySubgroup} {
   246  		c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   247  		// groups aren't frozen
   248  		c.Assert(ioutil.WriteFile(p, []byte("0"), 0644), IsNil)
   249  	}
   250  
   251  	c.Assert(cgroup.ThawSnapProcesses("foo"), IsNil)
   252  	for _, p := range []string{g1, g2, g3, g4} {
   253  		c.Check(p, testutil.FileEquals, "0")
   254  	}
   255  	// canaries are still unfrozen
   256  	c.Assert(canary, testutil.FileEquals, "0")
   257  	c.Assert(canarySubgroup, testutil.FileEquals, "0")
   258  
   259  	for _, p := range []string{g1, g2, g3, g4, canary, canarySubgroup} {
   260  		// make them appear frozen
   261  		c.Assert(ioutil.WriteFile(p, []byte("1"), 0644), IsNil)
   262  	}
   263  	c.Assert(cgroup.ThawSnapProcesses("foo"), IsNil)
   264  	for _, p := range []string{g1, g2, g3, g4} {
   265  		c.Check(p, testutil.FileEquals, "0")
   266  	}
   267  	c.Assert(canary, testutil.FileEquals, "1")
   268  	c.Assert(canarySubgroup, testutil.FileEquals, "1")
   269  
   270  	// freeze only some the groups groups
   271  	for _, p := range []string{g2, g3} {
   272  		c.Assert(ioutil.WriteFile(p, []byte("1"), 0644), IsNil)
   273  	}
   274  	c.Assert(cgroup.ThawSnapProcesses("foo"), IsNil)
   275  	// all are frozen again
   276  	for _, p := range []string{g1, g2, g3, g4} {
   277  		c.Check(p, testutil.FileEquals, "0")
   278  	}
   279  }
   280  
   281  func (s *freezerV2Suite) TestFreezeThawSnapProcessesV2ErrWalking(c *C) {
   282  	if os.Getuid() == 0 {
   283  		c.Skip("the test cannot be run by the root user")
   284  	}
   285  	defer cgroup.MockVersion(cgroup.V2, nil)()
   286  	dirs.SetRootDir(c.MkDir())
   287  	defer dirs.SetRootDir("")
   288  
   289  	// app started by root
   290  	g := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.1234-1234-1234.scope/cgroup.freeze")
   291  	gUnfreeze := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/cgroup.freeze")
   292  
   293  	pid := os.Getpid()
   294  	procPidCgroup := filepath.Join(dirs.GlobalRootDir, fmt.Sprintf("proc/%v/cgroup", pid))
   295  	c.Assert(os.MkdirAll(filepath.Dir(procPidCgroup), 0755), IsNil)
   296  	// mock our own group
   297  	c.Assert(ioutil.WriteFile(procPidCgroup, []byte("0::/system.slice/snap.foo.app.own-own-own.scope"), 0755), IsNil)
   298  	// prepare the stage
   299  	c.Assert(os.MkdirAll(filepath.Dir(g), 0755), IsNil)
   300  	c.Assert(ioutil.WriteFile(g, []byte("0"), 0644), IsNil)
   301  	c.Assert(os.MkdirAll(filepath.Dir(gUnfreeze), 0755), IsNil)
   302  	c.Assert(ioutil.WriteFile(gUnfreeze, []byte("1"), 0644), IsNil)
   303  
   304  	c.Assert(os.Chmod(filepath.Dir(g), 0000), IsNil)
   305  	// make the cleanup happy
   306  	defer os.Chmod(filepath.Dir(g), 0755)
   307  
   308  	// freeze tries thawing on errors, so we'll observe both errors
   309  	err := cgroup.FreezeSnapProcesses("foo")
   310  	// go 1.10+ slightly changed the order of calls in filepath.Walk(), make
   311  	// sure the error check matches both
   312  	c.Check(err, ErrorMatches, `cannot finish freezing processes of snap "foo":( cannot freeze processes of snap "foo",)? open .*/sys/fs/cgroup/system.slice/snap.foo.app.1234.1234.1234.scope(/cgroup.freeze)?: permission denied`)
   313  	// other group was unfrozen
   314  	c.Check(gUnfreeze, testutil.FileEquals, "0")
   315  
   316  	c.Assert(ioutil.WriteFile(gUnfreeze, []byte("1"), 0644), IsNil)
   317  	// make file access fail
   318  	c.Assert(os.Chmod(filepath.Dir(g), 0755), IsNil)
   319  	c.Assert(os.Chmod(g, 0000), IsNil)
   320  	// other group was unfrozen
   321  	err = cgroup.FreezeSnapProcesses("foo")
   322  	c.Check(err, ErrorMatches, `cannot finish freezing processes of snap "foo": cannot freeze processes of snap "foo", open .*/sys/fs/cgroup/system.slice/snap.foo.app.1234.1234.1234.scope/cgroup.freeze: permission denied`)
   323  	// other group was unfrozen
   324  	c.Check(gUnfreeze, testutil.FileEquals, "0")
   325  
   326  	// thawing fails likewise
   327  	err = cgroup.ThawSnapProcesses("foo")
   328  	c.Check(err, ErrorMatches, `cannot thaw processes of snap "foo", open .*/sys/fs/cgroup/system.slice/snap.foo.app.1234.1234.1234.scope/cgroup.freeze: permission denied`)
   329  	// other group was unfrozen
   330  	c.Check(gUnfreeze, testutil.FileEquals, "0")
   331  
   332  	// make unfreezing fail
   333  	c.Assert(ioutil.WriteFile(gUnfreeze, []byte("1"), 0644), IsNil)
   334  	c.Assert(os.Chmod(filepath.Dir(gUnfreeze), 0000), IsNil)
   335  	defer os.Chmod(filepath.Dir(gUnfreeze), 0755)
   336  
   337  	err = cgroup.FreezeSnapProcesses("foo")
   338  	// but the unfreeze errors are ignored anyuway
   339  	c.Check(err, ErrorMatches, `cannot finish freezing processes of snap "foo": cannot freeze processes of snap "foo", open .*/sys/fs/cgroup/system.slice/snap.foo.app.1234.1234.1234.scope/cgroup.freeze: permission denied`)
   340  	// the other group is unmodified
   341  	os.Chmod(filepath.Dir(gUnfreeze), 0755)
   342  	c.Check(gUnfreeze, testutil.FileEquals, "1")
   343  }
   344  
   345  func (s *freezerV2Suite) TestFreezeThawSnapProcessesV2ErrNotFound(c *C) {
   346  	defer cgroup.MockVersion(cgroup.V2, nil)()
   347  	dirs.SetRootDir(c.MkDir())
   348  	defer dirs.SetRootDir("")
   349  
   350  	// app started by root
   351  	g1 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.1234-1234-1234.scope/cgroup.freeze")
   352  	g2 := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.svc.service/cgroup.freeze")
   353  
   354  	pid := os.Getpid()
   355  	procPidCgroup := filepath.Join(dirs.GlobalRootDir, fmt.Sprintf("proc/%v/cgroup", pid))
   356  	c.Assert(os.MkdirAll(filepath.Dir(procPidCgroup), 0755), IsNil)
   357  	// mock our own group
   358  	c.Assert(ioutil.WriteFile(procPidCgroup, []byte("0::/system.slice/snap.foo.app.own-own-own.scope"), 0755), IsNil)
   359  	// prepare the directories, but not the files, those should trigger ENOENT
   360  	c.Assert(os.MkdirAll(filepath.Dir(g1), 0755), IsNil)
   361  	c.Assert(os.MkdirAll(filepath.Dir(g2), 0755), IsNil)
   362  
   363  	err := cgroup.FreezeSnapProcesses("foo")
   364  	c.Assert(err, IsNil)
   365  
   366  	c.Check(g1, testutil.FileAbsent)
   367  	c.Check(g2, testutil.FileAbsent)
   368  
   369  	err = cgroup.ThawSnapProcesses("foo")
   370  	c.Assert(err, IsNil)
   371  	c.Check(g1, testutil.FileAbsent)
   372  	c.Check(g2, testutil.FileAbsent)
   373  }
   374  
   375  func (s *freezerV2Suite) TestApplyToSnapCallbacks(c *C) {
   376  	defer cgroup.MockVersion(cgroup.V2, nil)()
   377  	dirs.SetRootDir(c.MkDir())
   378  	defer dirs.SetRootDir("")
   379  
   380  	c.Check(cgroup.ApplyToSnap("foo", nil, nil), ErrorMatches, "internal error: action is nil")
   381  	nop := func(_ string) error { return nil }
   382  	c.Check(cgroup.ApplyToSnap("foo", nop, nil), ErrorMatches, "internal error: skip error is nil")
   383  
   384  	g := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.1234-1234-1234.scope/cgroup.freeze")
   385  	gErr := filepath.Join(dirs.GlobalRootDir, "/sys/fs/cgroup/system.slice/snap.foo.app.fail.scope/cgroup.freeze")
   386  
   387  	for _, p := range []string{g, gErr} {
   388  		c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   389  		// groups aren't frozen
   390  		c.Assert(ioutil.WriteFile(p, []byte("0"), 0644), IsNil)
   391  	}
   392  
   393  	var visited []string
   394  	err := cgroup.ApplyToSnap("foo",
   395  		func(p string) error {
   396  			visited = append(visited, p)
   397  			return nil
   398  		},
   399  		func(err error) bool {
   400  			return true
   401  		})
   402  	c.Assert(err, IsNil)
   403  	sort.Strings(visited)
   404  	c.Check(visited, DeepEquals, []string{filepath.Dir(g), filepath.Dir(gErr)})
   405  
   406  	visited = nil
   407  	skip := true
   408  	var errors []string
   409  	maybeFail := func(p string) error {
   410  		visited = append(visited, p)
   411  		if strings.HasSuffix(p, "fail.scope") {
   412  			return fmt.Errorf("do not skip")
   413  		}
   414  		return nil
   415  	}
   416  	maybeSkip := func(err error) bool {
   417  		errors = append(errors, err.Error())
   418  		return skip
   419  	}
   420  	err = cgroup.ApplyToSnap("foo", maybeFail, maybeSkip)
   421  	c.Assert(err, IsNil)
   422  	c.Check(visited, DeepEquals, []string{filepath.Dir(g), filepath.Dir(gErr)})
   423  	c.Check(errors, DeepEquals, []string{"do not skip"})
   424  
   425  	skip = false
   426  	visited = nil
   427  	errors = nil
   428  	err = cgroup.ApplyToSnap("foo", maybeFail, maybeSkip)
   429  	c.Assert(err, ErrorMatches, "do not skip")
   430  	c.Check(visited, DeepEquals, []string{filepath.Dir(g), filepath.Dir(gErr)})
   431  	c.Check(errors, DeepEquals, []string{"do not skip"})
   432  }