github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/wrappers/services_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 wrappers_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"regexp"
    28  	"sort"
    29  	"strings"
    30  	"time"
    31  
    32  	. "gopkg.in/check.v1"
    33  
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/logger"
    36  	"github.com/snapcore/snapd/osutil"
    37  	"github.com/snapcore/snapd/progress"
    38  	"github.com/snapcore/snapd/snap"
    39  	"github.com/snapcore/snapd/snap/snaptest"
    40  	"github.com/snapcore/snapd/strutil"
    41  	"github.com/snapcore/snapd/systemd"
    42  	"github.com/snapcore/snapd/testutil"
    43  	"github.com/snapcore/snapd/timings"
    44  	"github.com/snapcore/snapd/usersession/agent"
    45  	"github.com/snapcore/snapd/wrappers"
    46  )
    47  
    48  type servicesTestSuite struct {
    49  	tempdir string
    50  
    51  	sysdLog [][]string
    52  
    53  	systemctlRestorer, delaysRestorer func()
    54  
    55  	perfTimings timings.Measurer
    56  
    57  	agent *agent.SessionAgent
    58  }
    59  
    60  var _ = Suite(&servicesTestSuite{})
    61  
    62  func (s *servicesTestSuite) SetUpTest(c *C) {
    63  	s.tempdir = c.MkDir()
    64  	s.sysdLog = nil
    65  	dirs.SetRootDir(s.tempdir)
    66  
    67  	s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
    68  		s.sysdLog = append(s.sysdLog, cmd)
    69  		return []byte("ActiveState=inactive\n"), nil
    70  	})
    71  	s.delaysRestorer = systemd.MockStopDelays(time.Millisecond, 25*time.Second)
    72  	s.perfTimings = timings.New(nil)
    73  
    74  	xdgRuntimeDir := fmt.Sprintf("%s/%d", dirs.XdgRuntimeDirBase, os.Getuid())
    75  	err := os.MkdirAll(xdgRuntimeDir, 0700)
    76  	c.Assert(err, IsNil)
    77  	s.agent, err = agent.New()
    78  	c.Assert(err, IsNil)
    79  	s.agent.Start()
    80  }
    81  
    82  func (s *servicesTestSuite) TearDownTest(c *C) {
    83  	if s.agent != nil {
    84  		err := s.agent.Stop()
    85  		c.Check(err, IsNil)
    86  	}
    87  	s.systemctlRestorer()
    88  	s.delaysRestorer()
    89  	dirs.SetRootDir("")
    90  }
    91  
    92  func (s *servicesTestSuite) TestAddSnapServicesAndRemove(c *C) {
    93  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
    94  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
    95  
    96  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
    97  	c.Assert(err, IsNil)
    98  	c.Check(s.sysdLog, DeepEquals, [][]string{
    99  		{"--root", dirs.GlobalRootDir, "enable", filepath.Base(svcFile)},
   100  		{"daemon-reload"},
   101  	})
   102  
   103  	content, err := ioutil.ReadFile(svcFile)
   104  	c.Assert(err, IsNil)
   105  
   106  	verbs := []string{"Start", "Stop", "StopPost"}
   107  	cmds := []string{"", " --command=stop", " --command=post-stop"}
   108  	for i := range verbs {
   109  		expected := fmt.Sprintf("Exec%s=/usr/bin/snap run%s hello-snap.svc1", verbs[i], cmds[i])
   110  		c.Check(string(content), Matches, "(?ms).*^"+regexp.QuoteMeta(expected)) // check.v1 adds ^ and $ around the regexp provided
   111  	}
   112  
   113  	s.sysdLog = nil
   114  	err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings)
   115  	c.Assert(err, IsNil)
   116  	c.Assert(s.sysdLog, HasLen, 2)
   117  	c.Check(s.sysdLog, DeepEquals, [][]string{
   118  		{"stop", filepath.Base(svcFile)},
   119  		{"show", "--property=ActiveState", "snap.hello-snap.svc1.service"},
   120  	})
   121  
   122  	s.sysdLog = nil
   123  	err = wrappers.RemoveSnapServices(info, progress.Null)
   124  	c.Assert(err, IsNil)
   125  	c.Check(osutil.FileExists(svcFile), Equals, false)
   126  	c.Assert(s.sysdLog, HasLen, 2)
   127  	c.Check(s.sysdLog[0], DeepEquals, []string{"--root", dirs.GlobalRootDir, "disable", filepath.Base(svcFile)})
   128  	c.Check(s.sysdLog[1], DeepEquals, []string{"daemon-reload"})
   129  }
   130  
   131  func (s *servicesTestSuite) TestAddSnapServicesAndRemoveUserDaemons(c *C) {
   132  	info := snaptest.MockSnap(c, packageHello+`
   133   svc1:
   134    daemon: simple
   135    daemon-scope: user
   136  `, &snap.SideInfo{Revision: snap.R(12)})
   137  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.service")
   138  
   139  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   140  	c.Assert(err, IsNil)
   141  	c.Check(s.sysdLog, DeepEquals, [][]string{
   142  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", filepath.Base(svcFile)},
   143  		{"--user", "daemon-reload"},
   144  	})
   145  
   146  	content, err := ioutil.ReadFile(svcFile)
   147  	c.Assert(err, IsNil)
   148  
   149  	expected := "ExecStart=/usr/bin/snap run hello-snap.svc1"
   150  	c.Check(string(content), Matches, "(?ms).*^"+regexp.QuoteMeta(expected)) // check.v1 adds ^ and $ around the regexp provided
   151  
   152  	s.sysdLog = nil
   153  	err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings)
   154  	c.Assert(err, IsNil)
   155  	c.Assert(s.sysdLog, HasLen, 2)
   156  	c.Check(s.sysdLog, DeepEquals, [][]string{
   157  		{"--user", "stop", filepath.Base(svcFile)},
   158  		{"--user", "show", "--property=ActiveState", "snap.hello-snap.svc1.service"},
   159  	})
   160  
   161  	s.sysdLog = nil
   162  	err = wrappers.RemoveSnapServices(info, progress.Null)
   163  	c.Assert(err, IsNil)
   164  	c.Check(osutil.FileExists(svcFile), Equals, false)
   165  	c.Assert(s.sysdLog, HasLen, 2)
   166  	c.Check(s.sysdLog, DeepEquals, [][]string{
   167  		{"--user", "--global", "--root", dirs.GlobalRootDir, "disable", filepath.Base(svcFile)},
   168  		{"--user", "daemon-reload"},
   169  	})
   170  }
   171  
   172  var snapdYaml = `name: snapd
   173  version: 1.0
   174  type: snapd
   175  `
   176  
   177  func (s *servicesTestSuite) TestRemoveSnapWithSocketsRemovesSocketsService(c *C) {
   178  	info := snaptest.MockSnap(c, packageHello+`
   179   svc1:
   180    daemon: simple
   181    plugs: [network-bind]
   182    sockets:
   183      sock1:
   184        listen-stream: $SNAP_DATA/sock1.socket
   185        socket-mode: 0666
   186      sock2:
   187        listen-stream: $SNAP_COMMON/sock2.socket
   188  `, &snap.SideInfo{Revision: snap.R(12)})
   189  
   190  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   191  	c.Assert(err, IsNil)
   192  
   193  	err = wrappers.StopServices(info.Services(), nil, "", &progress.Null, s.perfTimings)
   194  	c.Assert(err, IsNil)
   195  
   196  	err = wrappers.RemoveSnapServices(info, &progress.Null)
   197  	c.Assert(err, IsNil)
   198  
   199  	app := info.Apps["svc1"]
   200  	c.Assert(app.Sockets, HasLen, 2)
   201  	for _, socket := range app.Sockets {
   202  		c.Check(osutil.FileExists(socket.File()), Equals, false)
   203  	}
   204  }
   205  
   206  func (s *servicesTestSuite) TestRemoveSnapPackageFallbackToKill(c *C) {
   207  	restore := wrappers.MockKillWait(time.Millisecond)
   208  	defer restore()
   209  
   210  	var sysdLog [][]string
   211  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   212  		// filter out the "systemctl show" that
   213  		// StopServices generates
   214  		if cmd[0] != "show" {
   215  			sysdLog = append(sysdLog, cmd)
   216  		}
   217  		return []byte("ActiveState=active\n"), nil
   218  	})
   219  	defer r()
   220  
   221  	info := snaptest.MockSnap(c, `name: wat
   222  version: 42
   223  apps:
   224   wat:
   225     command: wat
   226     stop-timeout: 20ms
   227     daemon: forking
   228  `, &snap.SideInfo{Revision: snap.R(11)})
   229  
   230  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   231  	c.Assert(err, IsNil)
   232  
   233  	sysdLog = nil
   234  
   235  	svcFName := "snap.wat.wat.service"
   236  
   237  	err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings)
   238  	c.Assert(err, IsNil)
   239  
   240  	c.Check(sysdLog, DeepEquals, [][]string{
   241  		{"stop", svcFName},
   242  		// check kill invocations
   243  		{"kill", svcFName, "-s", "TERM", "--kill-who=all"},
   244  		{"kill", svcFName, "-s", "KILL", "--kill-who=all"},
   245  	})
   246  }
   247  
   248  func (s *servicesTestSuite) TestRemoveSnapPackageUserDaemonStopFailure(c *C) {
   249  	var sysdLog [][]string
   250  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   251  		// filter out the "systemctl --user show" that
   252  		// StopServices generates
   253  		if cmd[0] == "--user" && cmd[1] != "show" {
   254  			sysdLog = append(sysdLog, cmd)
   255  		}
   256  		if cmd[0] == "--user" && cmd[1] == "stop" {
   257  			return nil, fmt.Errorf("user unit stop failed")
   258  		}
   259  		return []byte("ActiveState=active\n"), nil
   260  	})
   261  	defer r()
   262  
   263  	info := snaptest.MockSnap(c, `name: wat
   264  version: 42
   265  apps:
   266   wat:
   267     command: wat
   268     stop-timeout: 20ms
   269     daemon: forking
   270     daemon-scope: user
   271  `, &snap.SideInfo{Revision: snap.R(11)})
   272  
   273  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   274  	c.Assert(err, IsNil)
   275  
   276  	sysdLog = nil
   277  
   278  	svcFName := "snap.wat.wat.service"
   279  
   280  	err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings)
   281  	c.Check(err, ErrorMatches, "some user services failed to stop")
   282  	c.Check(sysdLog, DeepEquals, [][]string{
   283  		{"--user", "stop", svcFName},
   284  	})
   285  }
   286  
   287  func (s *servicesTestSuite) TestServicesEnableState(c *C) {
   288  	info := snaptest.MockSnap(c, packageHello+`
   289   svc2:
   290    command: bin/hello
   291    daemon: forking
   292   svc3:
   293    command: bin/hello
   294    daemon: simple
   295    daemon-scope: user
   296  `, &snap.SideInfo{Revision: snap.R(12)})
   297  	svc1File := "snap.hello-snap.svc1.service"
   298  	svc2File := "snap.hello-snap.svc2.service"
   299  
   300  	s.systemctlRestorer()
   301  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   302  	if [ "$1" = "--root" ]; then
   303  		# shifting by 2 also drops the temp dir arg to --root
   304  	    shift 2
   305  	fi
   306  
   307  	case "$1" in
   308  		is-enabled)
   309  			case "$2" in 
   310  			"snap.hello-snap.svc1.service")
   311  				echo "disabled"
   312  				exit 1
   313  				;;
   314  			"snap.hello-snap.svc2.service")
   315  				echo "enabled"
   316  				exit 0
   317  				;;
   318  			*)
   319  				echo "unexpected is-enabled of service $2"
   320  				exit 2
   321  				;;
   322  			esac
   323  	        ;;
   324  	    *)
   325  	        echo "unexpected op $*"
   326  	        exit 2
   327  	esac
   328  
   329  	exit 1
   330  	`)
   331  	defer r.Restore()
   332  
   333  	states, err := wrappers.ServicesEnableState(info, progress.Null)
   334  	c.Assert(err, IsNil)
   335  
   336  	c.Assert(states, DeepEquals, map[string]bool{
   337  		"svc1": false,
   338  		"svc2": true,
   339  	})
   340  
   341  	// the calls could be out of order in the list, since iterating over a map
   342  	// is non-deterministic, so manually check each call
   343  	c.Assert(r.Calls(), HasLen, 2)
   344  	for _, call := range r.Calls() {
   345  		c.Assert(call, HasLen, 5)
   346  		c.Assert(call[:4], DeepEquals, []string{"systemctl", "--root", s.tempdir, "is-enabled"})
   347  		switch call[4] {
   348  		case svc1File, svc2File:
   349  		default:
   350  			c.Errorf("unknown service for systemctl call: %s", call[4])
   351  		}
   352  	}
   353  }
   354  
   355  func (s *servicesTestSuite) TestServicesEnableStateFail(c *C) {
   356  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   357  	svc1File := "snap.hello-snap.svc1.service"
   358  
   359  	s.systemctlRestorer()
   360  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   361  	if [ "$1" = "--root" ]; then
   362  		# shifting by 2 also drops the temp dir arg to --root
   363  	    shift 2
   364  	fi
   365  
   366  	case "$1" in
   367  		is-enabled)
   368  			case "$2" in
   369  			"snap.hello-snap.svc1.service")
   370  				echo "whoops"
   371  				exit 1
   372  				;;
   373  			*)
   374  				echo "unexpected is-enabled of service $2"
   375  				exit 2
   376  				;;
   377  			esac
   378  	        ;;
   379  	    *)
   380  	        echo "unexpected op $*"
   381  	        exit 2
   382  	esac
   383  
   384  	exit 1
   385  	`)
   386  	defer r.Restore()
   387  
   388  	_, err := wrappers.ServicesEnableState(info, progress.Null)
   389  	c.Assert(err, ErrorMatches, ".*is-enabled snap.hello-snap.svc1.service\\] failed with exit status 1: whoops\n.*")
   390  
   391  	c.Assert(r.Calls(), DeepEquals, [][]string{
   392  		{"systemctl", "--root", s.tempdir, "is-enabled", svc1File},
   393  	})
   394  }
   395  
   396  func (s *servicesTestSuite) TestAddSnapServicesWithDisabledServices(c *C) {
   397  	info := snaptest.MockSnap(c, packageHello+`
   398   svc2:
   399    command: bin/hello
   400    daemon: forking
   401  `, &snap.SideInfo{Revision: snap.R(12)})
   402  
   403  	s.systemctlRestorer()
   404  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   405  	if [ "$1" = "--root" ]; then
   406  	    shift 2
   407  	fi
   408  
   409  	case "$1" in
   410  		enable)
   411  			case "$2" in 
   412  				"snap.hello-snap.svc1.service")
   413  					echo "unexpected enable of disabled service $2"
   414  					exit 1
   415  					;;
   416  				"snap.hello-snap.svc2.service")
   417  					exit 0
   418  					;;
   419  				*)
   420  					echo "unexpected enable of service $2"
   421  					exit 1
   422  					;;
   423  			esac
   424  			;;
   425  		daemon-reload)
   426  			exit 0
   427  			;;
   428  	    *)
   429  	        echo "unexpected op $*"
   430  	        exit 2
   431  	esac
   432  	exit 2
   433  	`)
   434  	defer r.Restore()
   435  
   436  	// svc1 will be disabled
   437  	disabledSvcs := []string{"svc1"}
   438  
   439  	err := wrappers.AddSnapServices(info, disabledSvcs, nil, progress.Null)
   440  	c.Assert(err, IsNil)
   441  
   442  	// only svc2 should be enabled
   443  	c.Assert(r.Calls(), DeepEquals, [][]string{
   444  		{"systemctl", "--root", s.tempdir, "enable", "snap.hello-snap.svc2.service"},
   445  		{"systemctl", "daemon-reload"},
   446  	})
   447  }
   448  
   449  func (s *servicesTestSuite) TestAddSnapServicesWithDisabledServicesNowApp(c *C) {
   450  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   451  
   452  	// mock the logger
   453  	buf, loggerRestore := logger.MockLogger()
   454  	defer loggerRestore()
   455  
   456  	s.systemctlRestorer()
   457  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   458  	if [ "$1" = "--root" ]; then
   459  	    shift 2
   460  	fi
   461  
   462  	case "$1" in
   463  		enable)
   464  			case "$2" in 
   465  				"snap.hello-snap.svc1.service")
   466  					exit 0
   467  					;;
   468  				*)
   469  					echo "unexpected enable of service $2"
   470  					exit 1
   471  					;;
   472  			esac
   473  			;;
   474  		daemon-reload)
   475  			exit 0
   476  			;;
   477  	    *)
   478  	        echo "unexpected op $*"
   479  	        exit 2
   480  	esac
   481  	exit 2
   482  	`)
   483  	defer r.Restore()
   484  
   485  	svcs := []string{"hello"}
   486  
   487  	err := wrappers.AddSnapServices(info, svcs, nil, progress.Null)
   488  	c.Assert(err, IsNil)
   489  
   490  	// check the log for the notice
   491  	c.Assert(buf.String(), Matches, `.*previously disabled service hello is now an app and not a service\n.*`)
   492  
   493  	// the cleanup of AddSnapServices will remove written service files and then
   494  	// call reload, but note that we should catch any non-svc apps before
   495  	// actually enabling them, so we just see a daemon-reload call and not any
   496  	// enable calls
   497  	c.Assert(r.Calls(), DeepEquals, [][]string{
   498  		{"systemctl", "--root", s.tempdir, "enable", "snap.hello-snap.svc1.service"},
   499  		{"systemctl", "daemon-reload"},
   500  	})
   501  }
   502  
   503  func (s *servicesTestSuite) TestAddSnapServicesWithDisabledServicesMissing(c *C) {
   504  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   505  
   506  	// mock the logger
   507  	buf, loggerRestore := logger.MockLogger()
   508  	defer loggerRestore()
   509  
   510  	s.systemctlRestorer()
   511  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   512  	if [ "$1" = "--root" ]; then
   513  	    shift 2
   514  	fi
   515  
   516  	case "$1" in
   517  		enable)
   518  			case "$2" in 
   519  				"snap.hello-snap.svc1.service")
   520  					exit 0
   521  					;;
   522  				*)
   523  					echo "unexpected enable of service $2"
   524  					exit 1
   525  					;;
   526  			esac
   527  			;;
   528  		daemon-reload)
   529  			exit 0
   530  			;;
   531  	    *)
   532  	        echo "unexpected op $*"
   533  	        exit 2
   534  	esac
   535  	exit 2
   536  	`)
   537  	defer r.Restore()
   538  
   539  	svcs := []string{"old-disabled-svc"}
   540  
   541  	err := wrappers.AddSnapServices(info, svcs, nil, progress.Null)
   542  	c.Assert(err, IsNil)
   543  
   544  	// check the log for the notice
   545  	c.Assert(buf.String(), Matches, `.*previously disabled service old-disabled-svc no longer exists\n.*`)
   546  
   547  	// the cleanup of AddSnapServices will remove written service files and then
   548  	// call reload, but note that we should catch any non-svc apps before
   549  	// actually enabling them, so we just see a daemon-reload call and not any
   550  	// enable calls
   551  	c.Assert(r.Calls(), DeepEquals, [][]string{
   552  		{"systemctl", "--root", s.tempdir, "enable", "snap.hello-snap.svc1.service"},
   553  		{"systemctl", "daemon-reload"},
   554  	})
   555  }
   556  
   557  func (s *servicesTestSuite) TestAddSnapServicesWithPreseed(c *C) {
   558  	opts := &wrappers.AddSnapServicesOptions{Preseeding: true}
   559  
   560  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   561  
   562  	s.systemctlRestorer()
   563  	r := testutil.MockCommand(c, "systemctl", "exit 1")
   564  	defer r.Restore()
   565  
   566  	err := wrappers.AddSnapServices(info, nil, opts, progress.Null)
   567  	c.Assert(err, IsNil)
   568  
   569  	// file was created
   570  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.*.service"))
   571  	c.Check(svcFiles, HasLen, 1)
   572  
   573  	// but systemctl was not called
   574  	c.Assert(r.Calls(), HasLen, 0)
   575  }
   576  
   577  func (s *servicesTestSuite) TestStopServicesWithSockets(c *C) {
   578  	var sysServices, userServices []string
   579  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   580  		if cmd[0] == "stop" {
   581  			sysServices = append(sysServices, cmd[1])
   582  		} else if cmd[0] == "--user" && cmd[1] == "stop" {
   583  			userServices = append(userServices, cmd[2])
   584  		}
   585  		return []byte("ActiveState=inactive\n"), nil
   586  	})
   587  	defer r()
   588  
   589  	info := snaptest.MockSnap(c, packageHello+`
   590   svc1:
   591    daemon: simple
   592    plugs: [network-bind]
   593    sockets:
   594      sock1:
   595        listen-stream: $SNAP_COMMON/sock1.socket
   596        socket-mode: 0666
   597      sock2:
   598        listen-stream: $SNAP_DATA/sock2.socket
   599   svc2:
   600    daemon: simple
   601    daemon-scope: user
   602    plugs: [network-bind]
   603    sockets:
   604      sock1:
   605        listen-stream: $SNAP_USER_COMMON/sock1.socket
   606        socket-mode: 0666
   607      sock2:
   608        listen-stream: $SNAP_USER_DATA/sock2.socket
   609  `, &snap.SideInfo{Revision: snap.R(12)})
   610  
   611  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   612  	c.Assert(err, IsNil)
   613  
   614  	sysServices = nil
   615  	userServices = nil
   616  
   617  	err = wrappers.StopServices(info.Services(), nil, "", &progress.Null, s.perfTimings)
   618  	c.Assert(err, IsNil)
   619  
   620  	sort.Strings(sysServices)
   621  	c.Check(sysServices, DeepEquals, []string{
   622  		"snap.hello-snap.svc1.service", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"})
   623  	sort.Strings(userServices)
   624  	c.Check(userServices, DeepEquals, []string{
   625  		"snap.hello-snap.svc2.service", "snap.hello-snap.svc2.sock1.socket", "snap.hello-snap.svc2.sock2.socket"})
   626  }
   627  
   628  func (s *servicesTestSuite) TestStartServices(c *C) {
   629  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   630  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
   631  
   632  	err := wrappers.StartServices(info.Services(), nil, nil, &progress.Null, s.perfTimings)
   633  	c.Assert(err, IsNil)
   634  
   635  	c.Assert(s.sysdLog, DeepEquals, [][]string{
   636  		{"--root", s.tempdir, "is-enabled", filepath.Base(svcFile)},
   637  		{"start", filepath.Base(svcFile)},
   638  	})
   639  }
   640  
   641  func (s *servicesTestSuite) TestStartServicesUserDaemons(c *C) {
   642  	info := snaptest.MockSnap(c, packageHello+`
   643   svc1:
   644    daemon: simple
   645    daemon-scope: user
   646  `, &snap.SideInfo{Revision: snap.R(12)})
   647  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.service")
   648  
   649  	err := wrappers.StartServices(info.Services(), nil, nil, &progress.Null, s.perfTimings)
   650  	c.Assert(err, IsNil)
   651  
   652  	c.Assert(s.sysdLog, DeepEquals, [][]string{
   653  		{"--user", "--global", "--root", s.tempdir, "is-enabled", filepath.Base(svcFile)},
   654  		{"--user", "start", filepath.Base(svcFile)},
   655  	})
   656  }
   657  
   658  func (s *servicesTestSuite) TestEnableServices(c *C) {
   659  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   660  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
   661  
   662  	err := wrappers.EnableSnapServices(info, nil)
   663  	c.Assert(err, IsNil)
   664  
   665  	c.Assert(s.sysdLog, DeepEquals, [][]string{
   666  		{"--root", s.tempdir, "enable", filepath.Base(svcFile)},
   667  	})
   668  }
   669  
   670  func (s *servicesTestSuite) TestNoStartDisabledServices(c *C) {
   671  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   672  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
   673  
   674  	s.systemctlRestorer()
   675  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   676  	if [ "$1" = "--root" ]; then
   677  	    shift 2
   678  	fi
   679  
   680  	case "$1" in
   681  	    is-enabled)
   682  	        if [ "$2" = "snap.hello-snap.svc1.service" ]; then
   683  	            echo "disabled"
   684  	            exit 1
   685  	        else
   686  	            echo "unexpected call $*"
   687  	            exit 2
   688  	        fi
   689  	        ;;
   690  	    *)
   691  	        echo "unexpected call $*"
   692  	        exit 2
   693  	esac
   694  	`)
   695  	defer r.Restore()
   696  
   697  	err := wrappers.StartServices(info.Services(), nil, nil, &progress.Null, s.perfTimings)
   698  	c.Assert(err, IsNil)
   699  
   700  	c.Assert(r.Calls(), DeepEquals, [][]string{
   701  		{"systemctl", "--root", s.tempdir, "is-enabled", filepath.Base(svcFile)},
   702  	})
   703  }
   704  
   705  func (s *servicesTestSuite) TestAddSnapMultiServicesFailCreateCleanup(c *C) {
   706  	// sanity check: there are no service files
   707  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   708  	c.Check(svcFiles, HasLen, 0)
   709  
   710  	info := snaptest.MockSnap(c, packageHello+`
   711   svc2:
   712    daemon: potato
   713  `, &snap.SideInfo{Revision: snap.R(12)})
   714  
   715  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   716  	c.Assert(err, ErrorMatches, ".*potato.*")
   717  
   718  	// the services are cleaned up
   719  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   720  	c.Check(svcFiles, HasLen, 0)
   721  
   722  	// *either* the first service failed validation, and nothing
   723  	// was done, *or* the second one failed, and the first one was
   724  	// enabled before the second failed, and disabled after.
   725  	if len(s.sysdLog) > 0 {
   726  		// the second service failed validation
   727  		c.Check(s.sysdLog, DeepEquals, [][]string{
   728  			{"daemon-reload"},
   729  		})
   730  	}
   731  }
   732  
   733  func (s *servicesTestSuite) TestAddSnapMultiServicesFailEnableCleanup(c *C) {
   734  	var sysdLog [][]string
   735  	svc1Name := "snap.hello-snap.svc1.service"
   736  	svc2Name := "snap.hello-snap.svc2.service"
   737  	numEnables := 0
   738  
   739  	// sanity check: there are no service files
   740  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   741  	c.Check(svcFiles, HasLen, 0)
   742  
   743  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   744  		sysdLog = append(sysdLog, cmd)
   745  		sdcmd := cmd[0]
   746  		if len(cmd) >= 2 {
   747  			sdcmd = cmd[len(cmd)-2]
   748  		}
   749  		switch sdcmd {
   750  		case "enable":
   751  			numEnables++
   752  			switch numEnables {
   753  			case 1:
   754  				if cmd[len(cmd)-1] == svc2Name {
   755  					// the services are being iterated in the "wrong" order
   756  					svc1Name, svc2Name = svc2Name, svc1Name
   757  				}
   758  				return nil, nil
   759  			case 2:
   760  				return nil, fmt.Errorf("failed")
   761  			default:
   762  				panic("expected no more than 2 enables")
   763  			}
   764  		case "disable", "daemon-reload":
   765  			return nil, nil
   766  		default:
   767  			panic("unexpected systemctl command " + sdcmd)
   768  		}
   769  	})
   770  	defer r()
   771  
   772  	info := snaptest.MockSnap(c, packageHello+`
   773   svc2:
   774    command: bin/hello
   775    daemon: simple
   776  `, &snap.SideInfo{Revision: snap.R(12)})
   777  
   778  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   779  	c.Assert(err, ErrorMatches, "failed")
   780  
   781  	// the services are cleaned up
   782  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   783  	c.Check(svcFiles, HasLen, 0)
   784  	c.Check(sysdLog, DeepEquals, [][]string{
   785  		{"--root", dirs.GlobalRootDir, "enable", svc1Name},
   786  		{"--root", dirs.GlobalRootDir, "enable", svc2Name}, // this one fails
   787  		{"--root", dirs.GlobalRootDir, "disable", svc1Name},
   788  		{"daemon-reload"},
   789  	})
   790  }
   791  
   792  func (s *servicesTestSuite) TestAddSnapMultiServicesStartFailOnSystemdReloadCleanup(c *C) {
   793  	// this test might be overdoing it (it's mostly covering the same ground as the previous one), but ... :-)
   794  	var sysdLog [][]string
   795  	svc1Name := "snap.hello-snap.svc1.service"
   796  	svc2Name := "snap.hello-snap.svc2.service"
   797  
   798  	// sanity check: there are no service files
   799  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   800  	c.Check(svcFiles, HasLen, 0)
   801  
   802  	first := true
   803  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   804  		sysdLog = append(sysdLog, cmd)
   805  		if len(cmd) < 2 {
   806  			return nil, fmt.Errorf("failed")
   807  		}
   808  		if first {
   809  			first = false
   810  			if cmd[len(cmd)-1] == svc2Name {
   811  				// the services are being iterated in the "wrong" order
   812  				svc1Name, svc2Name = svc2Name, svc1Name
   813  			}
   814  		}
   815  		return nil, nil
   816  
   817  	})
   818  	defer r()
   819  
   820  	info := snaptest.MockSnap(c, packageHello+`
   821   svc2:
   822    command: bin/hello
   823    daemon: simple
   824  `, &snap.SideInfo{Revision: snap.R(12)})
   825  
   826  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   827  	c.Assert(err, ErrorMatches, "failed")
   828  
   829  	// the services are cleaned up
   830  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   831  	c.Check(svcFiles, HasLen, 0)
   832  	c.Check(sysdLog, DeepEquals, [][]string{
   833  		{"--root", dirs.GlobalRootDir, "enable", svc1Name},
   834  		{"--root", dirs.GlobalRootDir, "enable", svc2Name},
   835  		{"daemon-reload"}, // this one fails
   836  		{"--root", dirs.GlobalRootDir, "disable", svc1Name},
   837  		{"--root", dirs.GlobalRootDir, "disable", svc2Name},
   838  		{"daemon-reload"}, // so does this one :-)
   839  	})
   840  }
   841  
   842  func (s *servicesTestSuite) TestAddSnapMultiUserServicesFailEnableCleanup(c *C) {
   843  	var sysdLog [][]string
   844  	svc1Name := "snap.hello-snap.svc1.service"
   845  	svc2Name := "snap.hello-snap.svc2.service"
   846  	numEnables := 0
   847  
   848  	// sanity check: there are no service files
   849  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service"))
   850  	c.Check(svcFiles, HasLen, 0)
   851  
   852  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   853  		sysdLog = append(sysdLog, cmd)
   854  		if len(cmd) >= 1 && cmd[0] == "--user" {
   855  			cmd = cmd[1:]
   856  		}
   857  		if len(cmd) >= 1 && cmd[0] == "--global" {
   858  			cmd = cmd[1:]
   859  		}
   860  		sdcmd := cmd[0]
   861  		if len(cmd) >= 2 {
   862  			sdcmd = cmd[len(cmd)-2]
   863  		}
   864  		switch sdcmd {
   865  		case "enable":
   866  			numEnables++
   867  			switch numEnables {
   868  			case 1:
   869  				if cmd[len(cmd)-1] == svc2Name {
   870  					// the services are being iterated in the "wrong" order
   871  					svc1Name, svc2Name = svc2Name, svc1Name
   872  				}
   873  				return nil, nil
   874  			case 2:
   875  				return nil, fmt.Errorf("failed")
   876  			default:
   877  				panic("expected no more than 2 enables")
   878  			}
   879  		case "disable", "daemon-reload":
   880  			return nil, nil
   881  		default:
   882  			panic("unexpected systemctl command " + sdcmd)
   883  		}
   884  	})
   885  	defer r()
   886  
   887  	info := snaptest.MockSnap(c, packageHello+`
   888   svc1:
   889    command: bin/hello
   890    daemon: simple
   891    daemon-scope: user
   892   svc2:
   893    command: bin/hello
   894    daemon: simple
   895    daemon-scope: user
   896  `, &snap.SideInfo{Revision: snap.R(12)})
   897  
   898  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   899  	c.Assert(err, ErrorMatches, "failed")
   900  
   901  	// the services are cleaned up
   902  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service"))
   903  	c.Check(svcFiles, HasLen, 0)
   904  	c.Check(sysdLog, DeepEquals, [][]string{
   905  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", svc1Name},
   906  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", svc2Name}, // this one fails
   907  		{"--user", "--global", "--root", dirs.GlobalRootDir, "disable", svc1Name},
   908  		{"--user", "daemon-reload"},
   909  	})
   910  }
   911  
   912  func (s *servicesTestSuite) TestAddSnapMultiUserServicesStartFailOnSystemdReloadCleanup(c *C) {
   913  	// this test might be overdoing it (it's mostly covering the same ground as the previous one), but ... :-)
   914  	var sysdLog [][]string
   915  	svc1Name := "snap.hello-snap.svc1.service"
   916  	svc2Name := "snap.hello-snap.svc2.service"
   917  
   918  	// sanity check: there are no service files
   919  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service"))
   920  	c.Check(svcFiles, HasLen, 0)
   921  
   922  	first := true
   923  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   924  		sysdLog = append(sysdLog, cmd)
   925  		if len(cmd) < 3 {
   926  			return nil, fmt.Errorf("failed")
   927  		}
   928  		if first {
   929  			first = false
   930  			if cmd[len(cmd)-1] == svc2Name {
   931  				// the services are being iterated in the "wrong" order
   932  				svc1Name, svc2Name = svc2Name, svc1Name
   933  			}
   934  		}
   935  		return nil, nil
   936  
   937  	})
   938  	defer r()
   939  
   940  	info := snaptest.MockSnap(c, packageHello+`
   941   svc1:
   942    command: bin/hello
   943    daemon: simple
   944    daemon-scope: user
   945   svc2:
   946    command: bin/hello
   947    daemon: simple
   948    daemon-scope: user
   949  `, &snap.SideInfo{Revision: snap.R(12)})
   950  
   951  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   952  	c.Assert(err, ErrorMatches, "cannot reload daemon: failed")
   953  
   954  	// the services are cleaned up
   955  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service"))
   956  	c.Check(svcFiles, HasLen, 0)
   957  	c.Check(sysdLog, DeepEquals, [][]string{
   958  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", svc1Name},
   959  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", svc2Name},
   960  		{"--user", "daemon-reload"}, // this one fails
   961  		{"--user", "--global", "--root", dirs.GlobalRootDir, "disable", svc1Name},
   962  		{"--user", "--global", "--root", dirs.GlobalRootDir, "disable", svc2Name},
   963  		{"--user", "daemon-reload"}, // so does this one :-)
   964  	})
   965  }
   966  
   967  func (s *servicesTestSuite) TestAddSnapSocketFiles(c *C) {
   968  	info := snaptest.MockSnap(c, packageHello+`
   969   svc1:
   970    daemon: simple
   971    plugs: [network-bind]
   972    sockets:
   973      sock1:
   974        listen-stream: $SNAP_COMMON/sock1.socket
   975        socket-mode: 0666
   976      sock2:
   977        listen-stream: $SNAP_DATA/sock2.socket
   978      sock3:
   979        listen-stream: $XDG_RUNTIME_DIR/sock3.socket
   980  
   981  `, &snap.SideInfo{Revision: snap.R(12)})
   982  
   983  	sock1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock1.socket")
   984  	sock2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock2.socket")
   985  	sock3File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock3.socket")
   986  
   987  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
   988  	c.Assert(err, IsNil)
   989  
   990  	expected := fmt.Sprintf(
   991  		`[Socket]
   992  Service=snap.hello-snap.svc1.service
   993  FileDescriptorName=sock1
   994  ListenStream=%s
   995  SocketMode=0666
   996  
   997  `, filepath.Join(s.tempdir, "/var/snap/hello-snap/common/sock1.socket"))
   998  	c.Check(sock1File, testutil.FileContains, expected)
   999  
  1000  	expected = fmt.Sprintf(
  1001  		`[Socket]
  1002  Service=snap.hello-snap.svc1.service
  1003  FileDescriptorName=sock2
  1004  ListenStream=%s
  1005  
  1006  `, filepath.Join(s.tempdir, "/var/snap/hello-snap/12/sock2.socket"))
  1007  	c.Check(sock2File, testutil.FileContains, expected)
  1008  
  1009  	expected = fmt.Sprintf(
  1010  		`[Socket]
  1011  Service=snap.hello-snap.svc1.service
  1012  FileDescriptorName=sock3
  1013  ListenStream=%s
  1014  
  1015  `, filepath.Join(s.tempdir, "/run/user/0/snap.hello-snap/sock3.socket"))
  1016  	c.Check(sock3File, testutil.FileContains, expected)
  1017  }
  1018  
  1019  func (s *servicesTestSuite) TestAddSnapUserSocketFiles(c *C) {
  1020  	info := snaptest.MockSnap(c, packageHello+`
  1021   svc1:
  1022    daemon: simple
  1023    daemon-scope: user
  1024    plugs: [network-bind]
  1025    sockets:
  1026      sock1:
  1027        listen-stream: $SNAP_USER_COMMON/sock1.socket
  1028        socket-mode: 0666
  1029      sock2:
  1030        listen-stream: $SNAP_USER_DATA/sock2.socket
  1031      sock3:
  1032        listen-stream: $XDG_RUNTIME_DIR/sock3.socket
  1033  `, &snap.SideInfo{Revision: snap.R(12)})
  1034  
  1035  	sock1File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock1.socket")
  1036  	sock2File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock2.socket")
  1037  	sock3File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock3.socket")
  1038  
  1039  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1040  	c.Assert(err, IsNil)
  1041  
  1042  	expected := `[Socket]
  1043  Service=snap.hello-snap.svc1.service
  1044  FileDescriptorName=sock1
  1045  ListenStream=%h/snap/hello-snap/common/sock1.socket
  1046  SocketMode=0666
  1047  
  1048  `
  1049  	c.Check(sock1File, testutil.FileContains, expected)
  1050  
  1051  	expected = `[Socket]
  1052  Service=snap.hello-snap.svc1.service
  1053  FileDescriptorName=sock2
  1054  ListenStream=%h/snap/hello-snap/12/sock2.socket
  1055  
  1056  `
  1057  	c.Check(sock2File, testutil.FileContains, expected)
  1058  
  1059  	expected = `[Socket]
  1060  Service=snap.hello-snap.svc1.service
  1061  FileDescriptorName=sock3
  1062  ListenStream=%t/snap.hello-snap/sock3.socket
  1063  
  1064  `
  1065  	c.Check(sock3File, testutil.FileContains, expected)
  1066  }
  1067  
  1068  func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanup(c *C) {
  1069  	var sysdLog [][]string
  1070  	svc1Name := "snap.hello-snap.svc1.service"
  1071  	svc2Name := "snap.hello-snap.svc2.service"
  1072  
  1073  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1074  		sysdLog = append(sysdLog, cmd)
  1075  		if len(cmd) >= 2 && cmd[0] == "start" {
  1076  			name := cmd[len(cmd)-1]
  1077  			if name == svc2Name {
  1078  				return nil, fmt.Errorf("failed")
  1079  			}
  1080  		}
  1081  		return []byte("ActiveState=inactive\n"), nil
  1082  	})
  1083  	defer r()
  1084  
  1085  	info := snaptest.MockSnap(c, packageHello+`
  1086   svc2:
  1087    command: bin/hello
  1088    daemon: simple
  1089  `, &snap.SideInfo{Revision: snap.R(12)})
  1090  
  1091  	svcs := info.Services()
  1092  	c.Assert(svcs, HasLen, 2)
  1093  	if svcs[0].Name == "svc2" {
  1094  		svcs[0], svcs[1] = svcs[1], svcs[0]
  1095  	}
  1096  	err := wrappers.StartServices(svcs, nil, nil, &progress.Null, s.perfTimings)
  1097  	c.Assert(err, ErrorMatches, "failed")
  1098  	c.Assert(sysdLog, HasLen, 8, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
  1099  	c.Check(sysdLog, DeepEquals, [][]string{
  1100  		{"--root", s.tempdir, "is-enabled", svc1Name},
  1101  		{"--root", s.tempdir, "is-enabled", svc2Name},
  1102  		{"start", svc1Name},
  1103  		{"start", svc2Name}, // one of the services fails
  1104  		{"stop", svc2Name},
  1105  		{"show", "--property=ActiveState", svc2Name},
  1106  		{"stop", svc1Name},
  1107  		{"show", "--property=ActiveState", svc1Name},
  1108  	}, Commentf("calls: %v", sysdLog))
  1109  }
  1110  
  1111  func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanupWithSockets(c *C) {
  1112  	var sysdLog [][]string
  1113  	svc1Name := "snap.hello-snap.svc1.service"
  1114  	svc2Name := "snap.hello-snap.svc2.service"
  1115  	svc2SocketName := "snap.hello-snap.svc2.sock1.socket"
  1116  	svc3Name := "snap.hello-snap.svc3.service"
  1117  	svc3SocketName := "snap.hello-snap.svc3.sock1.socket"
  1118  
  1119  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1120  		sysdLog = append(sysdLog, cmd)
  1121  		c.Logf("call: %v", cmd)
  1122  		if len(cmd) >= 2 && cmd[0] == "start" && cmd[1] == svc3SocketName {
  1123  			// svc2 socket fails
  1124  			return nil, fmt.Errorf("failed")
  1125  		}
  1126  		return []byte("ActiveState=inactive\n"), nil
  1127  	})
  1128  	defer r()
  1129  
  1130  	info := snaptest.MockSnap(c, packageHello+`
  1131   svc2:
  1132    command: bin/hello
  1133    daemon: simple
  1134    sockets:
  1135      sock1:
  1136        listen-stream: $SNAP_COMMON/sock1.socket
  1137        socket-mode: 0666
  1138   svc3:
  1139    command: bin/hello
  1140    daemon: simple
  1141    sockets:
  1142      sock1:
  1143        listen-stream: $SNAP_COMMON/sock1.socket
  1144        socket-mode: 0666
  1145  `, &snap.SideInfo{Revision: snap.R(12)})
  1146  
  1147  	// ensure desired order
  1148  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]}
  1149  
  1150  	err := wrappers.StartServices(apps, nil, nil, &progress.Null, s.perfTimings)
  1151  	c.Assert(err, ErrorMatches, "failed")
  1152  	c.Logf("sysdlog: %v", sysdLog)
  1153  	c.Assert(sysdLog, HasLen, 17, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
  1154  	c.Check(sysdLog, DeepEquals, [][]string{
  1155  		{"--root", s.tempdir, "is-enabled", svc1Name},
  1156  		{"--root", s.tempdir, "enable", svc2SocketName},
  1157  		{"start", svc2SocketName},
  1158  		{"--root", s.tempdir, "enable", svc3SocketName},
  1159  		{"start", svc3SocketName}, // start failed, what follows is the cleanup
  1160  		{"stop", svc3SocketName},
  1161  		{"show", "--property=ActiveState", svc3SocketName},
  1162  		{"stop", svc3Name},
  1163  		{"show", "--property=ActiveState", svc3Name},
  1164  		{"--root", s.tempdir, "disable", svc3SocketName},
  1165  		{"stop", svc2SocketName},
  1166  		{"show", "--property=ActiveState", svc2SocketName},
  1167  		{"stop", svc2Name},
  1168  		{"show", "--property=ActiveState", svc2Name},
  1169  		{"--root", s.tempdir, "disable", svc2SocketName},
  1170  		{"stop", svc1Name},
  1171  		{"show", "--property=ActiveState", svc1Name},
  1172  	}, Commentf("calls: %v", sysdLog))
  1173  }
  1174  
  1175  func (s *servicesTestSuite) TestStartSnapMultiUserServicesFailStartCleanup(c *C) {
  1176  	var sysdLog [][]string
  1177  	svc1Name := "snap.hello-snap.svc1.service"
  1178  	svc2Name := "snap.hello-snap.svc2.service"
  1179  
  1180  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1181  		sysdLog = append(sysdLog, cmd)
  1182  		if len(cmd) >= 3 && cmd[0] == "--user" && cmd[1] == "start" {
  1183  			name := cmd[len(cmd)-1]
  1184  			if name == svc2Name {
  1185  				return nil, fmt.Errorf("failed")
  1186  			}
  1187  		}
  1188  		return []byte("ActiveState=inactive\n"), nil
  1189  	})
  1190  	defer r()
  1191  
  1192  	info := snaptest.MockSnap(c, packageHello+`
  1193   svc1:
  1194    command: bin/hello
  1195    daemon: simple
  1196    daemon-scope: user
  1197   svc2:
  1198    command: bin/hello
  1199    daemon: simple
  1200    daemon-scope: user
  1201  `, &snap.SideInfo{Revision: snap.R(12)})
  1202  
  1203  	svcs := info.Services()
  1204  	c.Assert(svcs, HasLen, 2)
  1205  	if svcs[0].Name == "svc2" {
  1206  		svcs[0], svcs[1] = svcs[1], svcs[0]
  1207  	}
  1208  	err := wrappers.StartServices(svcs, nil, nil, &progress.Null, s.perfTimings)
  1209  	c.Assert(err, ErrorMatches, "some user services failed to start")
  1210  	c.Assert(sysdLog, HasLen, 10, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
  1211  	c.Check(sysdLog, DeepEquals, [][]string{
  1212  		{"--user", "--global", "--root", s.tempdir, "is-enabled", svc1Name},
  1213  		{"--user", "--global", "--root", s.tempdir, "is-enabled", svc2Name},
  1214  		{"--user", "start", svc1Name},
  1215  		{"--user", "start", svc2Name}, // one of the services fails
  1216  		// session agent attempts to stop the non-failed services
  1217  		{"--user", "stop", svc1Name},
  1218  		{"--user", "show", "--property=ActiveState", svc1Name},
  1219  		// StartServices ensures everything is stopped
  1220  		{"--user", "stop", svc2Name},
  1221  		{"--user", "show", "--property=ActiveState", svc2Name},
  1222  		{"--user", "stop", svc1Name},
  1223  		{"--user", "show", "--property=ActiveState", svc1Name},
  1224  	}, Commentf("calls: %v", sysdLog))
  1225  }
  1226  
  1227  func (s *servicesTestSuite) TestStartSnapServicesKeepsOrder(c *C) {
  1228  	var sysdLog [][]string
  1229  	svc1Name := "snap.services-snap.svc1.service"
  1230  	svc2Name := "snap.services-snap.svc2.service"
  1231  	svc3Name := "snap.services-snap.svc3.service"
  1232  
  1233  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1234  		sysdLog = append(sysdLog, cmd)
  1235  		return []byte("ActiveState=inactive\n"), nil
  1236  	})
  1237  	defer r()
  1238  
  1239  	info := snaptest.MockSnap(c, `name: services-snap
  1240  apps:
  1241    svc1:
  1242      daemon: simple
  1243      before: [svc3]
  1244    svc2:
  1245      daemon: simple
  1246      after: [svc1]
  1247    svc3:
  1248      daemon: simple
  1249      before: [svc2]
  1250  `, &snap.SideInfo{Revision: snap.R(12)})
  1251  
  1252  	svcs := info.Services()
  1253  	c.Assert(svcs, HasLen, 3)
  1254  
  1255  	sorted, err := snap.SortServices(svcs)
  1256  	c.Assert(err, IsNil)
  1257  
  1258  	err = wrappers.StartServices(sorted, nil, nil, &progress.Null, s.perfTimings)
  1259  	c.Assert(err, IsNil)
  1260  	c.Assert(sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
  1261  	c.Check(sysdLog, DeepEquals, [][]string{
  1262  		{"--root", s.tempdir, "is-enabled", svc1Name},
  1263  		{"--root", s.tempdir, "is-enabled", svc3Name},
  1264  		{"--root", s.tempdir, "is-enabled", svc2Name},
  1265  		{"start", svc1Name},
  1266  		{"start", svc3Name},
  1267  		{"start", svc2Name},
  1268  	}, Commentf("calls: %v", sysdLog))
  1269  
  1270  	// change the order
  1271  	sorted[1], sorted[0] = sorted[0], sorted[1]
  1272  
  1273  	// we should observe the calls done in the same order as services
  1274  	err = wrappers.StartServices(sorted, nil, nil, &progress.Null, s.perfTimings)
  1275  	c.Assert(err, IsNil)
  1276  	c.Assert(sysdLog, HasLen, 12, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
  1277  	c.Check(sysdLog[6:], DeepEquals, [][]string{
  1278  		{"--root", s.tempdir, "is-enabled", svc3Name},
  1279  		{"--root", s.tempdir, "is-enabled", svc1Name},
  1280  		{"--root", s.tempdir, "is-enabled", svc2Name},
  1281  		{"start", svc3Name},
  1282  		{"start", svc1Name},
  1283  		{"start", svc2Name},
  1284  	}, Commentf("calls: %v", sysdLog))
  1285  }
  1286  
  1287  func (s *servicesTestSuite) TestServiceAfterBefore(c *C) {
  1288  	snapYaml := packageHello + `
  1289   svc2:
  1290     daemon: forking
  1291     after: [svc1]
  1292   svc3:
  1293     daemon: forking
  1294     before: [svc4]
  1295     after:  [svc2]
  1296   svc4:
  1297     daemon: forking
  1298     after:
  1299       - svc1
  1300       - svc2
  1301       - svc3
  1302  `
  1303  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
  1304  
  1305  	checks := []struct {
  1306  		file    string
  1307  		kind    string
  1308  		matches []string
  1309  	}{{
  1310  		file:    filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"),
  1311  		kind:    "After",
  1312  		matches: []string{info.Apps["svc1"].ServiceName()},
  1313  	}, {
  1314  		file:    filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"),
  1315  		kind:    "After",
  1316  		matches: []string{info.Apps["svc2"].ServiceName()},
  1317  	}, {
  1318  		file:    filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"),
  1319  		kind:    "Before",
  1320  		matches: []string{info.Apps["svc4"].ServiceName()},
  1321  	}, {
  1322  		file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"),
  1323  		kind: "After",
  1324  		matches: []string{
  1325  			info.Apps["svc1"].ServiceName(),
  1326  			info.Apps["svc2"].ServiceName(),
  1327  			info.Apps["svc3"].ServiceName(),
  1328  		},
  1329  	}}
  1330  
  1331  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1332  	c.Assert(err, IsNil)
  1333  
  1334  	for _, check := range checks {
  1335  		content, err := ioutil.ReadFile(check.file)
  1336  		c.Assert(err, IsNil)
  1337  
  1338  		for _, m := range check.matches {
  1339  			c.Check(string(content), Matches,
  1340  				// match:
  1341  				//   ...
  1342  				//   After=other.mount some.target foo.service bar.service
  1343  				//   Before=foo.service bar.service
  1344  				//   ...
  1345  				// but not:
  1346  				//   Foo=something After=foo.service Bar=something else
  1347  				// or:
  1348  				//   After=foo.service
  1349  				//   bar.service
  1350  				// or:
  1351  				//   After=  foo.service    bar.service
  1352  				"(?ms).*^(?U)"+check.kind+"=.*\\s?"+regexp.QuoteMeta(m)+"\\s?[^=]*$")
  1353  		}
  1354  	}
  1355  
  1356  }
  1357  
  1358  func (s *servicesTestSuite) TestServiceWatchdog(c *C) {
  1359  	snapYaml := packageHello + `
  1360   svc2:
  1361     daemon: forking
  1362     watchdog-timeout: 12s
  1363   svc3:
  1364     daemon: forking
  1365     watchdog-timeout: 0s
  1366   svc4:
  1367     daemon: forking
  1368  `
  1369  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
  1370  
  1371  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1372  	c.Assert(err, IsNil)
  1373  
  1374  	content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"))
  1375  	c.Assert(err, IsNil)
  1376  	c.Check(strings.Contains(string(content), "\nWatchdogSec=12\n"), Equals, true)
  1377  
  1378  	noWatchdog := []string{
  1379  		filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"),
  1380  		filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"),
  1381  	}
  1382  	for _, svcPath := range noWatchdog {
  1383  		content, err := ioutil.ReadFile(svcPath)
  1384  		c.Assert(err, IsNil)
  1385  		c.Check(strings.Contains(string(content), "WatchdogSec="), Equals, false)
  1386  	}
  1387  }
  1388  
  1389  func (s *servicesTestSuite) TestStopServiceEndure(c *C) {
  1390  	const surviveYaml = `name: survive-snap
  1391  version: 1.0
  1392  apps:
  1393   survivor:
  1394    command: bin/survivor
  1395    refresh-mode: endure
  1396    daemon: simple
  1397  `
  1398  	info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)})
  1399  	survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.survivor.service")
  1400  
  1401  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1402  	c.Assert(err, IsNil)
  1403  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1404  		{"--root", dirs.GlobalRootDir, "enable", filepath.Base(survivorFile)},
  1405  		{"daemon-reload"},
  1406  	})
  1407  
  1408  	s.sysdLog = nil
  1409  	err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRefresh, progress.Null, s.perfTimings)
  1410  	c.Assert(err, IsNil)
  1411  	c.Assert(s.sysdLog, HasLen, 0)
  1412  
  1413  	s.sysdLog = nil
  1414  	err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRemove, progress.Null, s.perfTimings)
  1415  	c.Assert(err, IsNil)
  1416  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1417  		{"stop", filepath.Base(survivorFile)},
  1418  		{"show", "--property=ActiveState", "snap.survive-snap.survivor.service"},
  1419  	})
  1420  
  1421  }
  1422  
  1423  func (s *servicesTestSuite) TestStopServiceSigs(c *C) {
  1424  	r := wrappers.MockKillWait(1 * time.Millisecond)
  1425  	defer r()
  1426  
  1427  	survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.srv.service")
  1428  	for _, t := range []struct {
  1429  		mode        string
  1430  		expectedSig string
  1431  		expectedWho string
  1432  	}{
  1433  		{mode: "sigterm", expectedSig: "TERM", expectedWho: "main"},
  1434  		{mode: "sigterm-all", expectedSig: "TERM", expectedWho: "all"},
  1435  		{mode: "sighup", expectedSig: "HUP", expectedWho: "main"},
  1436  		{mode: "sighup-all", expectedSig: "HUP", expectedWho: "all"},
  1437  		{mode: "sigusr1", expectedSig: "USR1", expectedWho: "main"},
  1438  		{mode: "sigusr1-all", expectedSig: "USR1", expectedWho: "all"},
  1439  		{mode: "sigusr2", expectedSig: "USR2", expectedWho: "main"},
  1440  		{mode: "sigusr2-all", expectedSig: "USR2", expectedWho: "all"},
  1441  	} {
  1442  		surviveYaml := fmt.Sprintf(`name: survive-snap
  1443  version: 1.0
  1444  apps:
  1445   srv:
  1446    command: bin/survivor
  1447    stop-mode: %s
  1448    daemon: simple
  1449  `, t.mode)
  1450  		info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)})
  1451  
  1452  		s.sysdLog = nil
  1453  		err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1454  		c.Assert(err, IsNil)
  1455  		c.Check(s.sysdLog, DeepEquals, [][]string{
  1456  			{"--root", dirs.GlobalRootDir, "enable", filepath.Base(survivorFile)},
  1457  			{"daemon-reload"},
  1458  		})
  1459  
  1460  		s.sysdLog = nil
  1461  		err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRefresh, progress.Null, s.perfTimings)
  1462  		c.Assert(err, IsNil)
  1463  		c.Check(s.sysdLog, DeepEquals, [][]string{
  1464  			{"stop", filepath.Base(survivorFile)},
  1465  			{"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
  1466  		}, Commentf("failure in %s", t.mode))
  1467  
  1468  		s.sysdLog = nil
  1469  		err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRemove, progress.Null, s.perfTimings)
  1470  		c.Assert(err, IsNil)
  1471  		switch t.expectedWho {
  1472  		case "all":
  1473  			c.Check(s.sysdLog, DeepEquals, [][]string{
  1474  				{"stop", filepath.Base(survivorFile)},
  1475  				{"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
  1476  			})
  1477  		case "main":
  1478  			c.Check(s.sysdLog, DeepEquals, [][]string{
  1479  				{"stop", filepath.Base(survivorFile)},
  1480  				{"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
  1481  				{"kill", filepath.Base(survivorFile), "-s", "TERM", "--kill-who=all"},
  1482  				{"kill", filepath.Base(survivorFile), "-s", "KILL", "--kill-who=all"},
  1483  			})
  1484  		default:
  1485  			panic("not reached")
  1486  		}
  1487  	}
  1488  
  1489  }
  1490  
  1491  func (s *servicesTestSuite) TestStartSnapSocketEnableStart(c *C) {
  1492  	svc1Name := "snap.hello-snap.svc1.service"
  1493  	// svc2Name := "snap.hello-snap.svc2.service"
  1494  	svc2Sock := "snap.hello-snap.svc2.sock.socket"
  1495  	svc3Sock := "snap.hello-snap.svc3.sock.socket"
  1496  
  1497  	info := snaptest.MockSnap(c, packageHello+`
  1498   svc2:
  1499    command: bin/hello
  1500    daemon: simple
  1501    sockets:
  1502      sock:
  1503        listen-stream: $SNAP_COMMON/sock1.socket
  1504   svc3:
  1505    command: bin/hello
  1506    daemon: simple
  1507    daemon-scope: user
  1508    sockets:
  1509      sock:
  1510        listen-stream: $SNAP_USER_COMMON/sock1.socket
  1511  `, &snap.SideInfo{Revision: snap.R(12)})
  1512  
  1513  	// fix the apps order to make the test stable
  1514  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]}
  1515  	err := wrappers.StartServices(apps, nil, nil, &progress.Null, s.perfTimings)
  1516  	c.Assert(err, IsNil)
  1517  	c.Assert(s.sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog))
  1518  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1519  		{"--root", dirs.GlobalRootDir, "is-enabled", svc1Name},
  1520  		{"--root", dirs.GlobalRootDir, "enable", svc2Sock},
  1521  		{"start", svc2Sock},
  1522  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", svc3Sock},
  1523  		{"--user", "start", svc3Sock},
  1524  		{"start", svc1Name},
  1525  	}, Commentf("calls: %v", s.sysdLog))
  1526  }
  1527  
  1528  func (s *servicesTestSuite) TestStartSnapTimerEnableStart(c *C) {
  1529  	svc1Name := "snap.hello-snap.svc1.service"
  1530  	// svc2Name := "snap.hello-snap.svc2.service"
  1531  	svc2Timer := "snap.hello-snap.svc2.timer"
  1532  	svc3Timer := "snap.hello-snap.svc3.timer"
  1533  
  1534  	info := snaptest.MockSnap(c, packageHello+`
  1535   svc2:
  1536    command: bin/hello
  1537    daemon: simple
  1538    timer: 10:00-12:00
  1539   svc3:
  1540    command: bin/hello
  1541    daemon: simple
  1542    daemon-scope: user
  1543    timer: 10:00-12:00
  1544  `, &snap.SideInfo{Revision: snap.R(12)})
  1545  
  1546  	// fix the apps order to make the test stable
  1547  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]}
  1548  	err := wrappers.StartServices(apps, nil, nil, &progress.Null, s.perfTimings)
  1549  	c.Assert(err, IsNil)
  1550  	c.Assert(s.sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog))
  1551  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1552  		{"--root", dirs.GlobalRootDir, "is-enabled", svc1Name},
  1553  		{"--root", dirs.GlobalRootDir, "enable", svc2Timer},
  1554  		{"start", svc2Timer},
  1555  		{"--user", "--global", "--root", dirs.GlobalRootDir, "enable", svc3Timer},
  1556  		{"--user", "start", svc3Timer},
  1557  		{"start", svc1Name},
  1558  	}, Commentf("calls: %v", s.sysdLog))
  1559  }
  1560  
  1561  func (s *servicesTestSuite) TestStartSnapTimerCleanup(c *C) {
  1562  	var sysdLog [][]string
  1563  	svc1Name := "snap.hello-snap.svc1.service"
  1564  	svc2Name := "snap.hello-snap.svc2.service"
  1565  	svc2Timer := "snap.hello-snap.svc2.timer"
  1566  
  1567  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1568  		sysdLog = append(sysdLog, cmd)
  1569  		if len(cmd) >= 2 && cmd[0] == "start" && cmd[1] == svc2Timer {
  1570  			return nil, fmt.Errorf("failed")
  1571  		}
  1572  		return []byte("ActiveState=inactive\n"), nil
  1573  	})
  1574  	defer r()
  1575  
  1576  	info := snaptest.MockSnap(c, packageHello+`
  1577   svc2:
  1578    command: bin/hello
  1579    daemon: simple
  1580    timer: 10:00-12:00
  1581  `, &snap.SideInfo{Revision: snap.R(12)})
  1582  
  1583  	// fix the apps order to make the test stable
  1584  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"]}
  1585  	err := wrappers.StartServices(apps, nil, nil, &progress.Null, s.perfTimings)
  1586  	c.Assert(err, ErrorMatches, "failed")
  1587  	c.Assert(sysdLog, HasLen, 10, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
  1588  	c.Check(sysdLog, DeepEquals, [][]string{
  1589  		{"--root", dirs.GlobalRootDir, "is-enabled", svc1Name},
  1590  		{"--root", dirs.GlobalRootDir, "enable", svc2Timer},
  1591  		{"start", svc2Timer}, // this call fails
  1592  		{"stop", svc2Timer},
  1593  		{"show", "--property=ActiveState", svc2Timer},
  1594  		{"stop", svc2Name},
  1595  		{"show", "--property=ActiveState", svc2Name},
  1596  		{"--root", dirs.GlobalRootDir, "disable", svc2Timer},
  1597  		{"stop", svc1Name},
  1598  		{"show", "--property=ActiveState", svc1Name},
  1599  	}, Commentf("calls: %v", sysdLog))
  1600  }
  1601  
  1602  func (s *servicesTestSuite) TestAddRemoveSnapWithTimersAddsRemovesTimerFiles(c *C) {
  1603  	info := snaptest.MockSnap(c, packageHello+`
  1604   svc2:
  1605    command: bin/hello
  1606    daemon: simple
  1607    timer: 10:00-12:00
  1608  `, &snap.SideInfo{Revision: snap.R(12)})
  1609  
  1610  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1611  	c.Assert(err, IsNil)
  1612  
  1613  	app := info.Apps["svc2"]
  1614  	c.Assert(app.Timer, NotNil)
  1615  
  1616  	c.Check(osutil.FileExists(app.Timer.File()), Equals, true)
  1617  	c.Check(osutil.FileExists(app.ServiceFile()), Equals, true)
  1618  
  1619  	err = wrappers.StopServices(info.Services(), nil, "", &progress.Null, s.perfTimings)
  1620  	c.Assert(err, IsNil)
  1621  
  1622  	err = wrappers.RemoveSnapServices(info, &progress.Null)
  1623  	c.Assert(err, IsNil)
  1624  
  1625  	c.Check(osutil.FileExists(app.Timer.File()), Equals, false)
  1626  	c.Check(osutil.FileExists(app.ServiceFile()), Equals, false)
  1627  }
  1628  
  1629  func (s *servicesTestSuite) TestFailedAddSnapCleansUp(c *C) {
  1630  	info := snaptest.MockSnap(c, packageHello+`
  1631   svc2:
  1632    command: bin/hello
  1633    daemon: simple
  1634    timer: 10:00-12:00
  1635   svc3:
  1636    command: bin/hello
  1637    daemon: simple
  1638    plugs: [network-bind]
  1639    sockets:
  1640      sock1:
  1641        listen-stream: $SNAP_COMMON/sock1.socket
  1642        socket-mode: 0666
  1643  `, &snap.SideInfo{Revision: snap.R(12)})
  1644  
  1645  	calls := 0
  1646  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1647  		if len(cmd) == 1 && cmd[0] == "daemon-reload" && calls == 0 {
  1648  			// only fail the first systemd daemon-reload call, the
  1649  			// second one is at the end of cleanup
  1650  			calls += 1
  1651  			return nil, fmt.Errorf("failed")
  1652  		}
  1653  		return []byte("ActiveState=inactive\n"), nil
  1654  	})
  1655  	defer r()
  1656  
  1657  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1658  	c.Assert(err, NotNil)
  1659  
  1660  	c.Logf("services dir: %v", dirs.SnapServicesDir)
  1661  	matches, err := filepath.Glob(dirs.SnapServicesDir + "/*")
  1662  	c.Assert(err, IsNil)
  1663  	c.Assert(matches, HasLen, 0, Commentf("the following autogenerated files were left behind: %v", matches))
  1664  }
  1665  
  1666  func (s *servicesTestSuite) TestAddServicesDidReload(c *C) {
  1667  	const base = `name: hello-snap
  1668  version: 1.10
  1669  summary: hello
  1670  description: Hello...
  1671  apps:
  1672  `
  1673  	onlyServices := snaptest.MockSnap(c, base+`
  1674   svc1:
  1675    command: bin/hello
  1676    daemon: simple
  1677  `, &snap.SideInfo{Revision: snap.R(12)})
  1678  
  1679  	onlySockets := snaptest.MockSnap(c, base+`
  1680   svc1:
  1681    command: bin/hello
  1682    daemon: simple
  1683    plugs: [network-bind]
  1684    sockets:
  1685      sock1:
  1686        listen-stream: $SNAP_COMMON/sock1.socket
  1687        socket-mode: 0666
  1688  `, &snap.SideInfo{Revision: snap.R(12)})
  1689  
  1690  	onlyTimers := snaptest.MockSnap(c, base+`
  1691   svc1:
  1692    command: bin/hello
  1693    daemon: oneshot
  1694    timer: 10:00-12:00
  1695  `, &snap.SideInfo{Revision: snap.R(12)})
  1696  
  1697  	for i, info := range []*snap.Info{onlyServices, onlySockets, onlyTimers} {
  1698  		s.sysdLog = nil
  1699  		err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1700  		c.Assert(err, IsNil)
  1701  		reloads := 0
  1702  		c.Logf("calls: %v", s.sysdLog)
  1703  		for _, call := range s.sysdLog {
  1704  			if strutil.ListContains(call, "daemon-reload") {
  1705  				reloads += 1
  1706  			}
  1707  		}
  1708  		c.Check(reloads >= 1, Equals, true, Commentf("test-case %v did not reload services as expected", i))
  1709  	}
  1710  }
  1711  
  1712  func (s *servicesTestSuite) TestSnapServicesActivation(c *C) {
  1713  	const snapYaml = `name: hello-snap
  1714  version: 1.10
  1715  summary: hello
  1716  description: Hello...
  1717  apps:
  1718   svc1:
  1719    command: bin/hello
  1720    daemon: simple
  1721    plugs: [network-bind]
  1722    sockets:
  1723      sock1:
  1724        listen-stream: $SNAP_COMMON/sock1.socket
  1725        socket-mode: 0666
  1726   svc2:
  1727    command: bin/hello
  1728    daemon: oneshot
  1729    timer: 10:00-12:00
  1730   svc3:
  1731    command: bin/hello
  1732    daemon: simple
  1733  `
  1734  
  1735  	svc3Name := "snap.hello-snap.svc3.service"
  1736  
  1737  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
  1738  
  1739  	// fix the apps order to make the test stable
  1740  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1741  	c.Assert(err, IsNil)
  1742  	c.Assert(s.sysdLog, HasLen, 2, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog))
  1743  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1744  		// only svc3 gets started during boot
  1745  		{"--root", dirs.GlobalRootDir, "enable", svc3Name},
  1746  		{"daemon-reload"},
  1747  	}, Commentf("calls: %v", s.sysdLog))
  1748  }
  1749  
  1750  func (s *servicesTestSuite) TestServiceRestartDelay(c *C) {
  1751  	snapYaml := packageHello + `
  1752   svc2:
  1753     daemon: forking
  1754     restart-delay: 12s
  1755   svc3:
  1756     daemon: forking
  1757  `
  1758  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
  1759  
  1760  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1761  	c.Assert(err, IsNil)
  1762  
  1763  	content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"))
  1764  	c.Assert(err, IsNil)
  1765  	c.Check(strings.Contains(string(content), "\nRestartSec=12\n"), Equals, true)
  1766  
  1767  	content, err = ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"))
  1768  	c.Assert(err, IsNil)
  1769  	c.Check(strings.Contains(string(content), "RestartSec="), Equals, false)
  1770  }
  1771  
  1772  func (s *servicesTestSuite) TestAddRemoveSnapServiceWithSnapd(c *C) {
  1773  	info := makeMockSnapdSnap(c)
  1774  
  1775  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1776  	c.Check(err, ErrorMatches, "internal error: adding explicit services for snapd snap is unexpected")
  1777  
  1778  	err = wrappers.RemoveSnapServices(info, progress.Null)
  1779  	c.Check(err, ErrorMatches, "internal error: removing explicit services for snapd snap is unexpected")
  1780  }
  1781  
  1782  func (s *servicesTestSuite) TestReloadOrRestart(c *C) {
  1783  	const surviveYaml = `name: test-snap
  1784  version: 1.0
  1785  apps:
  1786    foo:
  1787      command: bin/foo
  1788      daemon: simple
  1789  `
  1790  	info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)})
  1791  	srvFile := "snap.test-snap.foo.service"
  1792  
  1793  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1794  	c.Assert(err, IsNil)
  1795  
  1796  	s.sysdLog = nil
  1797  	flags := &wrappers.RestartServicesFlags{Reload: true}
  1798  	c.Assert(wrappers.RestartServices(info.Services(), flags, progress.Null, s.perfTimings), IsNil)
  1799  	c.Assert(err, IsNil)
  1800  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1801  		{"reload-or-restart", srvFile},
  1802  	})
  1803  
  1804  	s.sysdLog = nil
  1805  	flags.Reload = false
  1806  	c.Assert(wrappers.RestartServices(info.Services(), flags, progress.Null, s.perfTimings), IsNil)
  1807  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1808  		{"stop", srvFile},
  1809  		{"show", "--property=ActiveState", srvFile},
  1810  		{"start", srvFile},
  1811  	})
  1812  
  1813  	s.sysdLog = nil
  1814  	c.Assert(wrappers.RestartServices(info.Services(), nil, progress.Null, s.perfTimings), IsNil)
  1815  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1816  		{"stop", srvFile},
  1817  		{"show", "--property=ActiveState", srvFile},
  1818  		{"start", srvFile},
  1819  	})
  1820  }
  1821  
  1822  func (s *servicesTestSuite) TestStopAndDisableServices(c *C) {
  1823  	info := snaptest.MockSnap(c, packageHello+`
  1824   svc1:
  1825    daemon: simple
  1826  `, &snap.SideInfo{Revision: snap.R(12)})
  1827  	svcFile := "snap.hello-snap.svc1.service"
  1828  
  1829  	err := wrappers.AddSnapServices(info, nil, nil, progress.Null)
  1830  	c.Assert(err, IsNil)
  1831  
  1832  	s.sysdLog = nil
  1833  	flags := &wrappers.StopServicesFlags{Disable: true}
  1834  	err = wrappers.StopServices(info.Services(), flags, "", progress.Null, s.perfTimings)
  1835  	c.Assert(err, IsNil)
  1836  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1837  		{"stop", svcFile},
  1838  		{"show", "--property=ActiveState", svcFile},
  1839  		{"--root", s.tempdir, "disable", svcFile},
  1840  	})
  1841  }