github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/refresh_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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 snapstate_test
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/overlord/snapstate"
    33  	"github.com/snapcore/snapd/overlord/state"
    34  	"github.com/snapcore/snapd/snap"
    35  	"github.com/snapcore/snapd/snap/snaptest"
    36  )
    37  
    38  type refreshSuite struct {
    39  	state *state.State
    40  	info  *snap.Info
    41  
    42  	// paths of the PID cgroup of each app or hook.
    43  	daemonPath string
    44  	appPath    string
    45  	hookPath   string
    46  }
    47  
    48  var _ = Suite(&refreshSuite{})
    49  
    50  func (s *refreshSuite) SetUpTest(c *C) {
    51  	dirs.SetRootDir(c.MkDir())
    52  	yamlText := `
    53  name: foo
    54  version: 1
    55  apps:
    56    daemon:
    57      command: dummy
    58      daemon: simple
    59    app:
    60      command: dummy
    61  hooks:
    62    configure:
    63  `
    64  	s.info = snaptest.MockInfo(c, yamlText, nil)
    65  	s.daemonPath = filepath.Join(dirs.PidsCgroupDir, s.info.Apps["daemon"].SecurityTag())
    66  	s.appPath = filepath.Join(dirs.PidsCgroupDir, s.info.Apps["app"].SecurityTag())
    67  	s.hookPath = filepath.Join(dirs.PidsCgroupDir, s.info.Hooks["configure"].SecurityTag())
    68  }
    69  
    70  func (s *refreshSuite) TearDownTest(c *C) {
    71  	dirs.SetRootDir("")
    72  }
    73  
    74  func writePids(c *C, dir string, pids []int) {
    75  	err := os.MkdirAll(dir, 0755)
    76  	c.Assert(err, IsNil)
    77  	var buf bytes.Buffer
    78  	for _, pid := range pids {
    79  		fmt.Fprintf(&buf, "%d\n", pid)
    80  	}
    81  	err = ioutil.WriteFile(filepath.Join(dir, "cgroup.procs"), buf.Bytes(), 0644)
    82  	c.Assert(err, IsNil)
    83  }
    84  
    85  func (s *refreshSuite) TestSoftNothingRunningRefreshCheck(c *C) {
    86  	// There are no errors when PID cgroup is absent.
    87  	err := snapstate.SoftNothingRunningRefreshCheck(s.info)
    88  	c.Check(err, IsNil)
    89  
    90  	// Services are not blocking soft refresh check,
    91  	// they will be stopped before refresh.
    92  	writePids(c, s.daemonPath, []int{100})
    93  	err = snapstate.SoftNothingRunningRefreshCheck(s.info)
    94  	c.Check(err, IsNil)
    95  
    96  	// Apps are blocking soft refresh check. They are not stopped by
    97  	// snapd, unless the app is running for longer than the maximum
    98  	// duration allowed for postponing refreshes.
    99  	writePids(c, s.daemonPath, []int{100})
   100  	writePids(c, s.appPath, []int{101})
   101  	err = snapstate.SoftNothingRunningRefreshCheck(s.info)
   102  	c.Assert(err, NotNil)
   103  	c.Check(err.Error(), Equals, `snap "foo" has running apps (app)`)
   104  	c.Check(err.(*snapstate.BusySnapError).Pids(), DeepEquals, []int{101})
   105  
   106  	// Hooks behave just like apps. IDEA: perhaps hooks should not be
   107  	// killed this way? They have their own life-cycle management.
   108  	writePids(c, s.daemonPath, []int{})
   109  	writePids(c, s.appPath, []int{})
   110  	writePids(c, s.hookPath, []int{105})
   111  	err = snapstate.SoftNothingRunningRefreshCheck(s.info)
   112  	c.Assert(err, NotNil)
   113  	c.Check(err.Error(), Equals, `snap "foo" has running hooks (configure)`)
   114  	c.Check(err.(*snapstate.BusySnapError).Pids(), DeepEquals, []int{105})
   115  
   116  	// Both apps and hooks can be running.
   117  	writePids(c, s.daemonPath, []int{100})
   118  	writePids(c, s.appPath, []int{101})
   119  	writePids(c, s.hookPath, []int{105})
   120  	err = snapstate.SoftNothingRunningRefreshCheck(s.info)
   121  	c.Assert(err, NotNil)
   122  	c.Check(err.Error(), Equals, `snap "foo" has running apps (app) and hooks (configure)`)
   123  	c.Check(err.(*snapstate.BusySnapError).Pids(), DeepEquals, []int{101, 105})
   124  }
   125  
   126  func (s *refreshSuite) TestHardNothingRunningRefreshCheck(c *C) {
   127  	// There are no errors when PID cgroup is absent.
   128  	err := snapstate.HardNothingRunningRefreshCheck(s.info)
   129  	c.Check(err, IsNil)
   130  
   131  	// Regular services are blocking hard refresh check.
   132  	// We were expecting them to be gone by now.
   133  	writePids(c, s.daemonPath, []int{100})
   134  	writePids(c, s.appPath, []int{})
   135  	err = snapstate.HardNothingRunningRefreshCheck(s.info)
   136  	c.Assert(err, NotNil)
   137  	c.Check(err.Error(), Equals, `snap "foo" has running apps (daemon)`)
   138  	c.Check(err.(*snapstate.BusySnapError).Pids(), DeepEquals, []int{100})
   139  
   140  	// When the service is supposed to endure refreshes it will not be
   141  	// stopped. As such such services cannot block refresh.
   142  	s.info.Apps["daemon"].RefreshMode = "endure"
   143  	err = snapstate.HardNothingRunningRefreshCheck(s.info)
   144  	c.Check(err, IsNil)
   145  	s.info.Apps["daemon"].RefreshMode = ""
   146  
   147  	// Applications are also blocking hard refresh check.
   148  	writePids(c, s.daemonPath, []int{})
   149  	writePids(c, s.appPath, []int{101})
   150  	err = snapstate.HardNothingRunningRefreshCheck(s.info)
   151  	c.Assert(err, NotNil)
   152  	c.Check(err.Error(), Equals, `snap "foo" has running apps (app)`)
   153  	c.Check(err.(*snapstate.BusySnapError).Pids(), DeepEquals, []int{101})
   154  
   155  	// Hooks are equally blocking hard refresh check.
   156  	writePids(c, s.daemonPath, []int{})
   157  	writePids(c, s.appPath, []int{})
   158  	writePids(c, s.hookPath, []int{105})
   159  	err = snapstate.HardNothingRunningRefreshCheck(s.info)
   160  	c.Assert(err, NotNil)
   161  	c.Check(err.Error(), Equals, `snap "foo" has running hooks (configure)`)
   162  	c.Check(err.(*snapstate.BusySnapError).Pids(), DeepEquals, []int{105})
   163  }