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