github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  	"path/filepath"
    26  	"regexp"
    27  	"sort"
    28  	"strings"
    29  	"time"
    30  
    31  	. "gopkg.in/check.v1"
    32  
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/osutil"
    35  	"github.com/snapcore/snapd/progress"
    36  	"github.com/snapcore/snapd/snap"
    37  	"github.com/snapcore/snapd/snap/snaptest"
    38  	"github.com/snapcore/snapd/strutil"
    39  	"github.com/snapcore/snapd/systemd"
    40  	"github.com/snapcore/snapd/testutil"
    41  	"github.com/snapcore/snapd/timings"
    42  	"github.com/snapcore/snapd/wrappers"
    43  )
    44  
    45  type servicesTestSuite struct {
    46  	tempdir string
    47  
    48  	sysdLog [][]string
    49  
    50  	systemctlRestorer, delaysRestorer func()
    51  
    52  	perfTimings timings.Measurer
    53  }
    54  
    55  var _ = Suite(&servicesTestSuite{})
    56  
    57  func (s *servicesTestSuite) SetUpTest(c *C) {
    58  	s.tempdir = c.MkDir()
    59  	s.sysdLog = nil
    60  	dirs.SetRootDir(s.tempdir)
    61  
    62  	s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
    63  		s.sysdLog = append(s.sysdLog, cmd)
    64  		return []byte("ActiveState=inactive\n"), nil
    65  	})
    66  	s.delaysRestorer = systemd.MockStopDelays(time.Millisecond, 25*time.Second)
    67  	s.perfTimings = timings.New(nil)
    68  
    69  }
    70  
    71  func (s *servicesTestSuite) TearDownTest(c *C) {
    72  	dirs.SetRootDir("")
    73  	s.systemctlRestorer()
    74  	s.delaysRestorer()
    75  }
    76  
    77  func (s *servicesTestSuite) TestAddSnapServicesAndRemove(c *C) {
    78  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
    79  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
    80  
    81  	err := wrappers.AddSnapServices(info, nil)
    82  	c.Assert(err, IsNil)
    83  	c.Check(s.sysdLog, DeepEquals, [][]string{
    84  		{"--root", dirs.GlobalRootDir, "enable", filepath.Base(svcFile)},
    85  		{"daemon-reload"},
    86  	})
    87  
    88  	content, err := ioutil.ReadFile(svcFile)
    89  	c.Assert(err, IsNil)
    90  
    91  	verbs := []string{"Start", "Stop", "StopPost"}
    92  	cmds := []string{"", " --command=stop", " --command=post-stop"}
    93  	for i := range verbs {
    94  		expected := fmt.Sprintf("Exec%s=/usr/bin/snap run%s hello-snap.svc1", verbs[i], cmds[i])
    95  		c.Check(string(content), Matches, "(?ms).*^"+regexp.QuoteMeta(expected)) // check.v1 adds ^ and $ around the regexp provided
    96  	}
    97  
    98  	s.sysdLog = nil
    99  	err = wrappers.StopServices(info.Services(), "", progress.Null, s.perfTimings)
   100  	c.Assert(err, IsNil)
   101  	c.Assert(s.sysdLog, HasLen, 2)
   102  	c.Check(s.sysdLog, DeepEquals, [][]string{
   103  		{"stop", filepath.Base(svcFile)},
   104  		{"show", "--property=ActiveState", "snap.hello-snap.svc1.service"},
   105  	})
   106  
   107  	s.sysdLog = nil
   108  	err = wrappers.RemoveSnapServices(info, progress.Null)
   109  	c.Assert(err, IsNil)
   110  	c.Check(osutil.FileExists(svcFile), Equals, false)
   111  	c.Assert(s.sysdLog, HasLen, 2)
   112  	c.Check(s.sysdLog[0], DeepEquals, []string{"--root", dirs.GlobalRootDir, "disable", filepath.Base(svcFile)})
   113  	c.Check(s.sysdLog[1], DeepEquals, []string{"daemon-reload"})
   114  }
   115  
   116  var snapdYaml = `name: snapd
   117  version: 1.0
   118  type: snapd
   119  `
   120  
   121  func (s *servicesTestSuite) TestRemoveSnapWithSocketsRemovesSocketsService(c *C) {
   122  	info := snaptest.MockSnap(c, packageHello+`
   123   svc1:
   124    daemon: simple
   125    plugs: [network-bind]
   126    sockets:
   127      sock1:
   128        listen-stream: $SNAP_DATA/sock1.socket
   129        socket-mode: 0666
   130      sock2:
   131        listen-stream: $SNAP_COMMON/sock2.socket
   132  `, &snap.SideInfo{Revision: snap.R(12)})
   133  
   134  	err := wrappers.AddSnapServices(info, nil)
   135  	c.Assert(err, IsNil)
   136  
   137  	err = wrappers.StopServices(info.Services(), "", &progress.Null, s.perfTimings)
   138  	c.Assert(err, IsNil)
   139  
   140  	err = wrappers.RemoveSnapServices(info, &progress.Null)
   141  	c.Assert(err, IsNil)
   142  
   143  	app := info.Apps["svc1"]
   144  	c.Assert(app.Sockets, HasLen, 2)
   145  	for _, socket := range app.Sockets {
   146  		c.Check(osutil.FileExists(socket.File()), Equals, false)
   147  	}
   148  }
   149  
   150  func (s *servicesTestSuite) TestRemoveSnapPackageFallbackToKill(c *C) {
   151  	restore := wrappers.MockKillWait(time.Millisecond)
   152  	defer restore()
   153  
   154  	var sysdLog [][]string
   155  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   156  		// filter out the "systemctl show" that
   157  		// StopServices generates
   158  		if cmd[0] != "show" {
   159  			sysdLog = append(sysdLog, cmd)
   160  		}
   161  		return []byte("ActiveState=active\n"), nil
   162  	})
   163  	defer r()
   164  
   165  	info := snaptest.MockSnap(c, `name: wat
   166  version: 42
   167  apps:
   168   wat:
   169     command: wat
   170     stop-timeout: 20ms
   171     daemon: forking
   172  `, &snap.SideInfo{Revision: snap.R(11)})
   173  
   174  	err := wrappers.AddSnapServices(info, nil)
   175  	c.Assert(err, IsNil)
   176  
   177  	sysdLog = nil
   178  
   179  	svcFName := "snap.wat.wat.service"
   180  
   181  	err = wrappers.StopServices(info.Services(), "", progress.Null, s.perfTimings)
   182  	c.Assert(err, IsNil)
   183  
   184  	c.Check(sysdLog, DeepEquals, [][]string{
   185  		{"stop", svcFName},
   186  		// check kill invocations
   187  		{"kill", svcFName, "-s", "TERM", "--kill-who=all"},
   188  		{"kill", svcFName, "-s", "KILL", "--kill-who=all"},
   189  	})
   190  }
   191  
   192  func (s *servicesTestSuite) TestServicesEnableState(c *C) {
   193  	info := snaptest.MockSnap(c, packageHello+`
   194   svc2:
   195    command: bin/hello
   196    daemon: forking
   197  `, &snap.SideInfo{Revision: snap.R(12)})
   198  	svc1File := "snap.hello-snap.svc1.service"
   199  	svc2File := "snap.hello-snap.svc2.service"
   200  
   201  	s.systemctlRestorer()
   202  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   203  	if [ "$1" = "--root" ]; then
   204  		# shifting by 2 also drops the temp dir arg to --root
   205  	    shift 2
   206  	fi
   207  
   208  	case "$1" in
   209  		is-enabled)
   210  			case "$2" in 
   211  			"snap.hello-snap.svc1.service")
   212  				echo "disabled"
   213  				exit 1
   214  				;;
   215  			"snap.hello-snap.svc2.service")
   216  				echo "enabled"
   217  				exit 0
   218  				;;
   219  			*)
   220  				echo "unexpected service $*"
   221  				exit 2
   222  				;;
   223  			esac
   224  	        ;;
   225  	    *)
   226  	        echo "unexpected op $*"
   227  	        exit 2
   228  	esac
   229  
   230  	exit 1
   231  	`)
   232  	defer r.Restore()
   233  
   234  	states, err := wrappers.ServicesEnableState(info, progress.Null)
   235  	c.Assert(err, IsNil)
   236  
   237  	c.Assert(states, DeepEquals, map[string]bool{
   238  		"svc1": false,
   239  		"svc2": true,
   240  	})
   241  
   242  	// the calls could be out of order in the list, since iterating over a map
   243  	// is non-deterministic, so manually check each call
   244  	c.Assert(r.Calls(), HasLen, 2)
   245  	for _, call := range r.Calls() {
   246  		c.Assert(call, HasLen, 5)
   247  		c.Assert(call[:4], DeepEquals, []string{"systemctl", "--root", s.tempdir, "is-enabled"})
   248  		switch call[4] {
   249  		case svc1File, svc2File:
   250  		default:
   251  			c.Errorf("unknown service for systemctl call: %s", call[4])
   252  		}
   253  	}
   254  }
   255  
   256  func (s *servicesTestSuite) TestServicesEnableStateFail(c *C) {
   257  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   258  	svc1File := "snap.hello-snap.svc1.service"
   259  
   260  	s.systemctlRestorer()
   261  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   262  	if [ "$1" = "--root" ]; then
   263  		# shifting by 2 also drops the temp dir arg to --root
   264  	    shift 2
   265  	fi
   266  
   267  	case "$1" in
   268  		is-enabled)
   269  			case "$2" in
   270  			"snap.hello-snap.svc1.service")
   271  				echo "whoops"
   272  				exit 1
   273  				;;
   274  			*)
   275  				echo "unexpected service $*"
   276  				exit 2
   277  				;;
   278  			esac
   279  	        ;;
   280  	    *)
   281  	        echo "unexpected op $*"
   282  	        exit 2
   283  	esac
   284  
   285  	exit 1
   286  	`)
   287  	defer r.Restore()
   288  
   289  	_, err := wrappers.ServicesEnableState(info, progress.Null)
   290  	c.Assert(err, ErrorMatches, ".*is-enabled snap.hello-snap.svc1.service\\] failed with exit status 1: whoops\n.*")
   291  
   292  	c.Assert(r.Calls(), DeepEquals, [][]string{
   293  		{"systemctl", "--root", s.tempdir, "is-enabled", svc1File},
   294  	})
   295  }
   296  
   297  func (s *servicesTestSuite) TestStopServicesWithSockets(c *C) {
   298  	var sysdLog []string
   299  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   300  		if cmd[0] == "stop" {
   301  			sysdLog = append(sysdLog, cmd[1])
   302  		}
   303  		return []byte("ActiveState=inactive\n"), nil
   304  	})
   305  	defer r()
   306  
   307  	info := snaptest.MockSnap(c, packageHello+`
   308   svc1:
   309    daemon: simple
   310    plugs: [network-bind]
   311    sockets:
   312      sock1:
   313        listen-stream: $SNAP_COMMON/sock1.socket
   314        socket-mode: 0666
   315      sock2:
   316        listen-stream: $SNAP_DATA/sock2.socket
   317  `, &snap.SideInfo{Revision: snap.R(12)})
   318  
   319  	err := wrappers.AddSnapServices(info, nil)
   320  	c.Assert(err, IsNil)
   321  
   322  	sysdLog = nil
   323  
   324  	err = wrappers.StopServices(info.Services(), "", &progress.Null, s.perfTimings)
   325  	c.Assert(err, IsNil)
   326  
   327  	sort.Strings(sysdLog)
   328  	c.Check(sysdLog, DeepEquals, []string{
   329  		"snap.hello-snap.svc1.service", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"})
   330  }
   331  
   332  func (s *servicesTestSuite) TestStartServices(c *C) {
   333  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   334  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
   335  
   336  	err := wrappers.StartServices(info.Services(), nil, s.perfTimings)
   337  	c.Assert(err, IsNil)
   338  
   339  	c.Assert(s.sysdLog, DeepEquals, [][]string{
   340  		{"--root", s.tempdir, "is-enabled", filepath.Base(svcFile)},
   341  		{"start", filepath.Base(svcFile)},
   342  	})
   343  }
   344  
   345  func (s *servicesTestSuite) TestNoStartDisabledServices(c *C) {
   346  	info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)})
   347  	svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
   348  
   349  	s.systemctlRestorer()
   350  	r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
   351  	if [ "$1" = "--root" ]; then
   352  	    shift 2
   353  	fi
   354  
   355  	case "$1" in
   356  	    is-enabled)
   357  	        if [ "$2" = "snap.hello-snap.svc1.service" ]; then
   358  	            echo "disabled"
   359  	            exit 1
   360  	        else
   361  	            echo "unexpected call $*"
   362  	            exit 2
   363  	        fi
   364  	        ;;
   365  	    *)
   366  	        echo "unexpected call $*"
   367  	        exit 2
   368  	esac
   369  	`)
   370  	defer r.Restore()
   371  
   372  	err := wrappers.StartServices(info.Services(), nil, s.perfTimings)
   373  	c.Assert(err, IsNil)
   374  
   375  	c.Assert(r.Calls(), DeepEquals, [][]string{
   376  		{"systemctl", "--root", s.tempdir, "is-enabled", filepath.Base(svcFile)},
   377  	})
   378  }
   379  
   380  func (s *servicesTestSuite) TestAddSnapMultiServicesFailCreateCleanup(c *C) {
   381  	// sanity check: there are no service files
   382  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   383  	c.Check(svcFiles, HasLen, 0)
   384  
   385  	info := snaptest.MockSnap(c, packageHello+`
   386   svc2:
   387    daemon: potato
   388  `, &snap.SideInfo{Revision: snap.R(12)})
   389  
   390  	err := wrappers.AddSnapServices(info, nil)
   391  	c.Assert(err, ErrorMatches, ".*potato.*")
   392  
   393  	// the services are cleaned up
   394  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   395  	c.Check(svcFiles, HasLen, 0)
   396  
   397  	// *either* the first service failed validation, and nothing
   398  	// was done, *or* the second one failed, and the first one was
   399  	// enabled before the second failed, and disabled after.
   400  	if len(s.sysdLog) > 0 {
   401  		// the second service failed validation
   402  		c.Check(s.sysdLog, DeepEquals, [][]string{
   403  			{"--root", dirs.GlobalRootDir, "enable", "snap.hello-snap.svc1.service"},
   404  			{"--root", dirs.GlobalRootDir, "disable", "snap.hello-snap.svc1.service"},
   405  			{"daemon-reload"},
   406  		})
   407  	}
   408  }
   409  
   410  func (s *servicesTestSuite) TestAddSnapMultiServicesFailEnableCleanup(c *C) {
   411  	var sysdLog [][]string
   412  	svc1Name := "snap.hello-snap.svc1.service"
   413  	svc2Name := "snap.hello-snap.svc2.service"
   414  	numEnables := 0
   415  
   416  	// sanity check: there are no service files
   417  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   418  	c.Check(svcFiles, HasLen, 0)
   419  
   420  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   421  		sysdLog = append(sysdLog, cmd)
   422  		sdcmd := cmd[0]
   423  		if len(cmd) >= 2 {
   424  			sdcmd = cmd[len(cmd)-2]
   425  		}
   426  		switch sdcmd {
   427  		case "enable":
   428  			numEnables++
   429  			switch numEnables {
   430  			case 1:
   431  				if cmd[len(cmd)-1] == svc2Name {
   432  					// the services are being iterated in the "wrong" order
   433  					svc1Name, svc2Name = svc2Name, svc1Name
   434  				}
   435  				return nil, nil
   436  			case 2:
   437  				return nil, fmt.Errorf("failed")
   438  			default:
   439  				panic("expected no more than 2 enables")
   440  			}
   441  		case "disable", "daemon-reload":
   442  			return nil, nil
   443  		default:
   444  			panic("unexpected systemctl command " + sdcmd)
   445  		}
   446  	})
   447  	defer r()
   448  
   449  	info := snaptest.MockSnap(c, packageHello+`
   450   svc2:
   451    command: bin/hello
   452    daemon: simple
   453  `, &snap.SideInfo{Revision: snap.R(12)})
   454  
   455  	err := wrappers.AddSnapServices(info, nil)
   456  	c.Assert(err, ErrorMatches, "failed")
   457  
   458  	// the services are cleaned up
   459  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   460  	c.Check(svcFiles, HasLen, 0)
   461  	c.Check(sysdLog, DeepEquals, [][]string{
   462  		{"--root", dirs.GlobalRootDir, "enable", svc1Name},
   463  		{"--root", dirs.GlobalRootDir, "enable", svc2Name}, // this one fails
   464  		{"--root", dirs.GlobalRootDir, "disable", svc1Name},
   465  		{"daemon-reload"},
   466  	})
   467  }
   468  
   469  func (s *servicesTestSuite) TestAddSnapMultiServicesStartFailOnSystemdReloadCleanup(c *C) {
   470  	// this test might be overdoing it (it's mostly covering the same ground as the previous one), but ... :-)
   471  	var sysdLog [][]string
   472  	svc1Name := "snap.hello-snap.svc1.service"
   473  	svc2Name := "snap.hello-snap.svc2.service"
   474  
   475  	// sanity check: there are no service files
   476  	svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   477  	c.Check(svcFiles, HasLen, 0)
   478  
   479  	first := true
   480  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   481  		sysdLog = append(sysdLog, cmd)
   482  		if len(cmd) < 2 {
   483  			return nil, fmt.Errorf("failed")
   484  		}
   485  		if first {
   486  			first = false
   487  			if cmd[len(cmd)-1] == svc2Name {
   488  				// the services are being iterated in the "wrong" order
   489  				svc1Name, svc2Name = svc2Name, svc1Name
   490  			}
   491  		}
   492  		return nil, nil
   493  
   494  	})
   495  	defer r()
   496  
   497  	info := snaptest.MockSnap(c, packageHello+`
   498   svc2:
   499    command: bin/hello
   500    daemon: simple
   501  `, &snap.SideInfo{Revision: snap.R(12)})
   502  
   503  	err := wrappers.AddSnapServices(info, progress.Null)
   504  	c.Assert(err, ErrorMatches, "failed")
   505  
   506  	// the services are cleaned up
   507  	svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service"))
   508  	c.Check(svcFiles, HasLen, 0)
   509  	c.Check(sysdLog, DeepEquals, [][]string{
   510  		{"--root", dirs.GlobalRootDir, "enable", svc1Name},
   511  		{"--root", dirs.GlobalRootDir, "enable", svc2Name},
   512  		{"daemon-reload"}, // this one fails
   513  		{"--root", dirs.GlobalRootDir, "disable", svc1Name},
   514  		{"--root", dirs.GlobalRootDir, "disable", svc2Name},
   515  		{"daemon-reload"}, // so does this one :-)
   516  	})
   517  }
   518  
   519  func (s *servicesTestSuite) TestAddSnapSocketFiles(c *C) {
   520  	info := snaptest.MockSnap(c, packageHello+`
   521   svc1:
   522    daemon: simple
   523    plugs: [network-bind]
   524    sockets:
   525      sock1:
   526        listen-stream: $SNAP_COMMON/sock1.socket
   527        socket-mode: 0666
   528      sock2:
   529        listen-stream: $SNAP_DATA/sock2.socket
   530      sock3:
   531        listen-stream: $XDG_RUNTIME_DIR/sock3.socket
   532  
   533  `, &snap.SideInfo{Revision: snap.R(12)})
   534  
   535  	sock1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock1.socket")
   536  	sock2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock2.socket")
   537  	sock3File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock3.socket")
   538  
   539  	err := wrappers.AddSnapServices(info, nil)
   540  	c.Assert(err, IsNil)
   541  
   542  	expected := fmt.Sprintf(
   543  		`[Socket]
   544  Service=snap.hello-snap.svc1.service
   545  FileDescriptorName=sock1
   546  ListenStream=%s
   547  SocketMode=0666
   548  
   549  `, filepath.Join(s.tempdir, "/var/snap/hello-snap/common/sock1.socket"))
   550  	c.Check(sock1File, testutil.FileContains, expected)
   551  
   552  	expected = fmt.Sprintf(
   553  		`[Socket]
   554  Service=snap.hello-snap.svc1.service
   555  FileDescriptorName=sock2
   556  ListenStream=%s
   557  
   558  `, filepath.Join(s.tempdir, "/var/snap/hello-snap/12/sock2.socket"))
   559  	c.Check(sock2File, testutil.FileContains, expected)
   560  
   561  	expected = fmt.Sprintf(
   562  		`[Socket]
   563  Service=snap.hello-snap.svc1.service
   564  FileDescriptorName=sock3
   565  ListenStream=%s
   566  
   567  `, filepath.Join(s.tempdir, "/run/user/0/snap.hello-snap/sock3.socket"))
   568  	c.Check(sock3File, testutil.FileContains, expected)
   569  }
   570  
   571  func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanup(c *C) {
   572  	var sysdLog [][]string
   573  	svc1Name := "snap.hello-snap.svc1.service"
   574  	svc2Name := "snap.hello-snap.svc2.service"
   575  
   576  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   577  		sysdLog = append(sysdLog, cmd)
   578  		if len(cmd) >= 2 && cmd[0] == "start" {
   579  			name := cmd[len(cmd)-1]
   580  			if name == svc2Name {
   581  				return nil, fmt.Errorf("failed")
   582  			}
   583  		}
   584  		return []byte("ActiveState=inactive\n"), nil
   585  	})
   586  	defer r()
   587  
   588  	info := snaptest.MockSnap(c, packageHello+`
   589   svc2:
   590    command: bin/hello
   591    daemon: simple
   592  `, &snap.SideInfo{Revision: snap.R(12)})
   593  
   594  	svcs := info.Services()
   595  	c.Assert(svcs, HasLen, 2)
   596  	if svcs[0].Name == "svc2" {
   597  		svcs[0], svcs[1] = svcs[1], svcs[0]
   598  	}
   599  	err := wrappers.StartServices(svcs, nil, s.perfTimings)
   600  	c.Assert(err, ErrorMatches, "failed")
   601  	c.Assert(sysdLog, HasLen, 8, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
   602  	c.Check(sysdLog, DeepEquals, [][]string{
   603  		{"--root", s.tempdir, "is-enabled", svc1Name},
   604  		{"--root", s.tempdir, "is-enabled", svc2Name},
   605  		{"start", svc1Name},
   606  		{"start", svc2Name}, // one of the services fails
   607  		{"stop", svc2Name},
   608  		{"show", "--property=ActiveState", svc2Name},
   609  		{"stop", svc1Name},
   610  		{"show", "--property=ActiveState", svc1Name},
   611  	}, Commentf("calls: %v", sysdLog))
   612  }
   613  
   614  func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanupWithSockets(c *C) {
   615  	var sysdLog [][]string
   616  	svc1Name := "snap.hello-snap.svc1.service"
   617  	svc2Name := "snap.hello-snap.svc2.service"
   618  	svc2SocketName := "snap.hello-snap.svc2.sock1.socket"
   619  	svc3Name := "snap.hello-snap.svc3.service"
   620  	svc3SocketName := "snap.hello-snap.svc3.sock1.socket"
   621  
   622  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   623  		sysdLog = append(sysdLog, cmd)
   624  		c.Logf("call: %v", cmd)
   625  		if len(cmd) >= 2 && cmd[0] == "start" && cmd[1] == svc3SocketName {
   626  			// svc2 socket fails
   627  			return nil, fmt.Errorf("failed")
   628  		}
   629  		return []byte("ActiveState=inactive\n"), nil
   630  	})
   631  	defer r()
   632  
   633  	info := snaptest.MockSnap(c, packageHello+`
   634   svc2:
   635    command: bin/hello
   636    daemon: simple
   637    sockets:
   638      sock1:
   639        listen-stream: $SNAP_COMMON/sock1.socket
   640        socket-mode: 0666
   641   svc3:
   642    command: bin/hello
   643    daemon: simple
   644    sockets:
   645      sock1:
   646        listen-stream: $SNAP_COMMON/sock1.socket
   647        socket-mode: 0666
   648  `, &snap.SideInfo{Revision: snap.R(12)})
   649  
   650  	// ensure desired order
   651  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]}
   652  
   653  	err := wrappers.StartServices(apps, nil, s.perfTimings)
   654  	c.Assert(err, ErrorMatches, "failed")
   655  	c.Logf("sysdlog: %v", sysdLog)
   656  	c.Assert(sysdLog, HasLen, 17, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
   657  	c.Check(sysdLog, DeepEquals, [][]string{
   658  		{"--root", s.tempdir, "is-enabled", svc1Name},
   659  		{"--root", s.tempdir, "enable", svc2SocketName},
   660  		{"start", svc2SocketName},
   661  		{"--root", s.tempdir, "enable", svc3SocketName},
   662  		{"start", svc3SocketName}, // start failed, what follows is the cleanup
   663  		{"stop", svc3SocketName},
   664  		{"show", "--property=ActiveState", svc3SocketName},
   665  		{"stop", svc3Name},
   666  		{"show", "--property=ActiveState", svc3Name},
   667  		{"--root", s.tempdir, "disable", svc3SocketName},
   668  		{"stop", svc2SocketName},
   669  		{"show", "--property=ActiveState", svc2SocketName},
   670  		{"stop", svc2Name},
   671  		{"show", "--property=ActiveState", svc2Name},
   672  		{"--root", s.tempdir, "disable", svc2SocketName},
   673  		{"stop", svc1Name},
   674  		{"show", "--property=ActiveState", svc1Name},
   675  	}, Commentf("calls: %v", sysdLog))
   676  }
   677  
   678  func (s *servicesTestSuite) TestStartSnapServicesKeepsOrder(c *C) {
   679  	var sysdLog [][]string
   680  	svc1Name := "snap.services-snap.svc1.service"
   681  	svc2Name := "snap.services-snap.svc2.service"
   682  	svc3Name := "snap.services-snap.svc3.service"
   683  
   684  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   685  		sysdLog = append(sysdLog, cmd)
   686  		return []byte("ActiveState=inactive\n"), nil
   687  	})
   688  	defer r()
   689  
   690  	info := snaptest.MockSnap(c, `name: services-snap
   691  apps:
   692    svc1:
   693      daemon: simple
   694      before: [svc3]
   695    svc2:
   696      daemon: simple
   697      after: [svc1]
   698    svc3:
   699      daemon: simple
   700      before: [svc2]
   701  `, &snap.SideInfo{Revision: snap.R(12)})
   702  
   703  	svcs := info.Services()
   704  	c.Assert(svcs, HasLen, 3)
   705  
   706  	sorted, err := snap.SortServices(svcs)
   707  	c.Assert(err, IsNil)
   708  
   709  	err = wrappers.StartServices(sorted, nil, s.perfTimings)
   710  	c.Assert(err, IsNil)
   711  	c.Assert(sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
   712  	c.Check(sysdLog, DeepEquals, [][]string{
   713  		{"--root", s.tempdir, "is-enabled", svc1Name},
   714  		{"--root", s.tempdir, "is-enabled", svc3Name},
   715  		{"--root", s.tempdir, "is-enabled", svc2Name},
   716  		{"start", svc1Name},
   717  		{"start", svc3Name},
   718  		{"start", svc2Name},
   719  	}, Commentf("calls: %v", sysdLog))
   720  
   721  	// change the order
   722  	sorted[1], sorted[0] = sorted[0], sorted[1]
   723  
   724  	// we should observe the calls done in the same order as services
   725  	err = wrappers.StartServices(sorted, nil, s.perfTimings)
   726  	c.Assert(err, IsNil)
   727  	c.Assert(sysdLog, HasLen, 12, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
   728  	c.Check(sysdLog[6:], DeepEquals, [][]string{
   729  		{"--root", s.tempdir, "is-enabled", svc3Name},
   730  		{"--root", s.tempdir, "is-enabled", svc1Name},
   731  		{"--root", s.tempdir, "is-enabled", svc2Name},
   732  		{"start", svc3Name},
   733  		{"start", svc1Name},
   734  		{"start", svc2Name},
   735  	}, Commentf("calls: %v", sysdLog))
   736  }
   737  
   738  func (s *servicesTestSuite) TestServiceAfterBefore(c *C) {
   739  	snapYaml := packageHello + `
   740   svc2:
   741     daemon: forking
   742     after: [svc1]
   743   svc3:
   744     daemon: forking
   745     before: [svc4]
   746     after:  [svc2]
   747   svc4:
   748     daemon: forking
   749     after:
   750       - svc1
   751       - svc2
   752       - svc3
   753  `
   754  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
   755  
   756  	checks := []struct {
   757  		file    string
   758  		kind    string
   759  		matches []string
   760  	}{{
   761  		file:    filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"),
   762  		kind:    "After",
   763  		matches: []string{info.Apps["svc1"].ServiceName()},
   764  	}, {
   765  		file:    filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"),
   766  		kind:    "After",
   767  		matches: []string{info.Apps["svc2"].ServiceName()},
   768  	}, {
   769  		file:    filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"),
   770  		kind:    "Before",
   771  		matches: []string{info.Apps["svc4"].ServiceName()},
   772  	}, {
   773  		file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"),
   774  		kind: "After",
   775  		matches: []string{
   776  			info.Apps["svc1"].ServiceName(),
   777  			info.Apps["svc2"].ServiceName(),
   778  			info.Apps["svc3"].ServiceName(),
   779  		},
   780  	}}
   781  
   782  	err := wrappers.AddSnapServices(info, nil)
   783  	c.Assert(err, IsNil)
   784  
   785  	for _, check := range checks {
   786  		content, err := ioutil.ReadFile(check.file)
   787  		c.Assert(err, IsNil)
   788  
   789  		for _, m := range check.matches {
   790  			c.Check(string(content), Matches,
   791  				// match:
   792  				//   ...
   793  				//   After=other.mount some.target foo.service bar.service
   794  				//   Before=foo.service bar.service
   795  				//   ...
   796  				// but not:
   797  				//   Foo=something After=foo.service Bar=something else
   798  				// or:
   799  				//   After=foo.service
   800  				//   bar.service
   801  				// or:
   802  				//   After=  foo.service    bar.service
   803  				"(?ms).*^(?U)"+check.kind+"=.*\\s?"+regexp.QuoteMeta(m)+"\\s?[^=]*$")
   804  		}
   805  	}
   806  
   807  }
   808  
   809  func (s *servicesTestSuite) TestServiceWatchdog(c *C) {
   810  	snapYaml := packageHello + `
   811   svc2:
   812     daemon: forking
   813     watchdog-timeout: 12s
   814   svc3:
   815     daemon: forking
   816     watchdog-timeout: 0s
   817   svc4:
   818     daemon: forking
   819  `
   820  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
   821  
   822  	err := wrappers.AddSnapServices(info, nil)
   823  	c.Assert(err, IsNil)
   824  
   825  	content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"))
   826  	c.Assert(err, IsNil)
   827  	c.Check(strings.Contains(string(content), "\nWatchdogSec=12\n"), Equals, true)
   828  
   829  	noWatchdog := []string{
   830  		filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"),
   831  		filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"),
   832  	}
   833  	for _, svcPath := range noWatchdog {
   834  		content, err := ioutil.ReadFile(svcPath)
   835  		c.Assert(err, IsNil)
   836  		c.Check(strings.Contains(string(content), "WatchdogSec="), Equals, false)
   837  	}
   838  }
   839  
   840  func (s *servicesTestSuite) TestStopServiceEndure(c *C) {
   841  	const surviveYaml = `name: survive-snap
   842  version: 1.0
   843  apps:
   844   survivor:
   845    command: bin/survivor
   846    refresh-mode: endure
   847    daemon: simple
   848  `
   849  	info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)})
   850  	survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.survivor.service")
   851  
   852  	err := wrappers.AddSnapServices(info, nil)
   853  	c.Assert(err, IsNil)
   854  	c.Check(s.sysdLog, DeepEquals, [][]string{
   855  		{"--root", dirs.GlobalRootDir, "enable", filepath.Base(survivorFile)},
   856  		{"daemon-reload"},
   857  	})
   858  
   859  	s.sysdLog = nil
   860  	err = wrappers.StopServices(info.Services(), snap.StopReasonRefresh, progress.Null, s.perfTimings)
   861  	c.Assert(err, IsNil)
   862  	c.Assert(s.sysdLog, HasLen, 0)
   863  
   864  	s.sysdLog = nil
   865  	err = wrappers.StopServices(info.Services(), snap.StopReasonRemove, progress.Null, s.perfTimings)
   866  	c.Assert(err, IsNil)
   867  	c.Check(s.sysdLog, DeepEquals, [][]string{
   868  		{"stop", filepath.Base(survivorFile)},
   869  		{"show", "--property=ActiveState", "snap.survive-snap.survivor.service"},
   870  	})
   871  
   872  }
   873  
   874  func (s *servicesTestSuite) TestStopServiceSigs(c *C) {
   875  	r := wrappers.MockKillWait(1 * time.Millisecond)
   876  	defer r()
   877  
   878  	survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.srv.service")
   879  	for _, t := range []struct {
   880  		mode        string
   881  		expectedSig string
   882  		expectedWho string
   883  	}{
   884  		{mode: "sigterm", expectedSig: "TERM", expectedWho: "main"},
   885  		{mode: "sigterm-all", expectedSig: "TERM", expectedWho: "all"},
   886  		{mode: "sighup", expectedSig: "HUP", expectedWho: "main"},
   887  		{mode: "sighup-all", expectedSig: "HUP", expectedWho: "all"},
   888  		{mode: "sigusr1", expectedSig: "USR1", expectedWho: "main"},
   889  		{mode: "sigusr1-all", expectedSig: "USR1", expectedWho: "all"},
   890  		{mode: "sigusr2", expectedSig: "USR2", expectedWho: "main"},
   891  		{mode: "sigusr2-all", expectedSig: "USR2", expectedWho: "all"},
   892  	} {
   893  		surviveYaml := fmt.Sprintf(`name: survive-snap
   894  version: 1.0
   895  apps:
   896   srv:
   897    command: bin/survivor
   898    stop-mode: %s
   899    daemon: simple
   900  `, t.mode)
   901  		info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)})
   902  
   903  		s.sysdLog = nil
   904  		err := wrappers.AddSnapServices(info, nil)
   905  		c.Assert(err, IsNil)
   906  		c.Check(s.sysdLog, DeepEquals, [][]string{
   907  			{"--root", dirs.GlobalRootDir, "enable", filepath.Base(survivorFile)},
   908  			{"daemon-reload"},
   909  		})
   910  
   911  		s.sysdLog = nil
   912  		err = wrappers.StopServices(info.Services(), snap.StopReasonRefresh, progress.Null, s.perfTimings)
   913  		c.Assert(err, IsNil)
   914  		c.Check(s.sysdLog, DeepEquals, [][]string{
   915  			{"stop", filepath.Base(survivorFile)},
   916  			{"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
   917  		}, Commentf("failure in %s", t.mode))
   918  
   919  		s.sysdLog = nil
   920  		err = wrappers.StopServices(info.Services(), snap.StopReasonRemove, progress.Null, s.perfTimings)
   921  		c.Assert(err, IsNil)
   922  		switch t.expectedWho {
   923  		case "all":
   924  			c.Check(s.sysdLog, DeepEquals, [][]string{
   925  				{"stop", filepath.Base(survivorFile)},
   926  				{"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
   927  			})
   928  		case "main":
   929  			c.Check(s.sysdLog, DeepEquals, [][]string{
   930  				{"stop", filepath.Base(survivorFile)},
   931  				{"show", "--property=ActiveState", "snap.survive-snap.srv.service"},
   932  				{"kill", filepath.Base(survivorFile), "-s", "TERM", "--kill-who=all"},
   933  				{"kill", filepath.Base(survivorFile), "-s", "KILL", "--kill-who=all"},
   934  			})
   935  		default:
   936  			panic("not reached")
   937  		}
   938  	}
   939  
   940  }
   941  
   942  func (s *servicesTestSuite) TestStartSnapTimerEnableStart(c *C) {
   943  	svc1Name := "snap.hello-snap.svc1.service"
   944  	// svc2Name := "snap.hello-snap.svc2.service"
   945  	svc2Timer := "snap.hello-snap.svc2.timer"
   946  
   947  	info := snaptest.MockSnap(c, packageHello+`
   948   svc2:
   949    command: bin/hello
   950    daemon: simple
   951    timer: 10:00-12:00
   952  `, &snap.SideInfo{Revision: snap.R(12)})
   953  
   954  	// fix the apps order to make the test stable
   955  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"]}
   956  	err := wrappers.StartServices(apps, nil, s.perfTimings)
   957  	c.Assert(err, IsNil)
   958  	c.Assert(s.sysdLog, HasLen, 4, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog))
   959  	c.Check(s.sysdLog, DeepEquals, [][]string{
   960  		{"--root", dirs.GlobalRootDir, "is-enabled", svc1Name},
   961  		{"--root", dirs.GlobalRootDir, "enable", svc2Timer},
   962  		{"start", svc2Timer},
   963  		{"start", svc1Name},
   964  	}, Commentf("calls: %v", s.sysdLog))
   965  }
   966  
   967  func (s *servicesTestSuite) TestStartSnapTimerCleanup(c *C) {
   968  	var sysdLog [][]string
   969  	svc1Name := "snap.hello-snap.svc1.service"
   970  	svc2Name := "snap.hello-snap.svc2.service"
   971  	svc2Timer := "snap.hello-snap.svc2.timer"
   972  
   973  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   974  		sysdLog = append(sysdLog, cmd)
   975  		if len(cmd) >= 2 && cmd[0] == "start" && cmd[1] == svc2Timer {
   976  			return nil, fmt.Errorf("failed")
   977  		}
   978  		return []byte("ActiveState=inactive\n"), nil
   979  	})
   980  	defer r()
   981  
   982  	info := snaptest.MockSnap(c, packageHello+`
   983   svc2:
   984    command: bin/hello
   985    daemon: simple
   986    timer: 10:00-12:00
   987  `, &snap.SideInfo{Revision: snap.R(12)})
   988  
   989  	// fix the apps order to make the test stable
   990  	apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"]}
   991  	err := wrappers.StartServices(apps, nil, s.perfTimings)
   992  	c.Assert(err, ErrorMatches, "failed")
   993  	c.Assert(sysdLog, HasLen, 10, Commentf("len: %v calls: %v", len(sysdLog), sysdLog))
   994  	c.Check(sysdLog, DeepEquals, [][]string{
   995  		{"--root", dirs.GlobalRootDir, "is-enabled", svc1Name},
   996  		{"--root", dirs.GlobalRootDir, "enable", svc2Timer},
   997  		{"start", svc2Timer}, // this call fails
   998  		{"stop", svc2Timer},
   999  		{"show", "--property=ActiveState", svc2Timer},
  1000  		{"stop", svc2Name},
  1001  		{"show", "--property=ActiveState", svc2Name},
  1002  		{"--root", dirs.GlobalRootDir, "disable", svc2Timer},
  1003  		{"stop", svc1Name},
  1004  		{"show", "--property=ActiveState", svc1Name},
  1005  	}, Commentf("calls: %v", sysdLog))
  1006  }
  1007  
  1008  func (s *servicesTestSuite) TestAddRemoveSnapWithTimersAddsRemovesTimerFiles(c *C) {
  1009  	info := snaptest.MockSnap(c, packageHello+`
  1010   svc2:
  1011    command: bin/hello
  1012    daemon: simple
  1013    timer: 10:00-12:00
  1014  `, &snap.SideInfo{Revision: snap.R(12)})
  1015  
  1016  	err := wrappers.AddSnapServices(info, nil)
  1017  	c.Assert(err, IsNil)
  1018  
  1019  	app := info.Apps["svc2"]
  1020  	c.Assert(app.Timer, NotNil)
  1021  
  1022  	c.Check(osutil.FileExists(app.Timer.File()), Equals, true)
  1023  	c.Check(osutil.FileExists(app.ServiceFile()), Equals, true)
  1024  
  1025  	err = wrappers.StopServices(info.Services(), "", &progress.Null, s.perfTimings)
  1026  	c.Assert(err, IsNil)
  1027  
  1028  	err = wrappers.RemoveSnapServices(info, &progress.Null)
  1029  	c.Assert(err, IsNil)
  1030  
  1031  	c.Check(osutil.FileExists(app.Timer.File()), Equals, false)
  1032  	c.Check(osutil.FileExists(app.ServiceFile()), Equals, false)
  1033  }
  1034  
  1035  func (s *servicesTestSuite) TestFailedAddSnapCleansUp(c *C) {
  1036  	info := snaptest.MockSnap(c, packageHello+`
  1037   svc2:
  1038    command: bin/hello
  1039    daemon: simple
  1040    timer: 10:00-12:00
  1041   svc3:
  1042    command: bin/hello
  1043    daemon: simple
  1044    plugs: [network-bind]
  1045    sockets:
  1046      sock1:
  1047        listen-stream: $SNAP_COMMON/sock1.socket
  1048        socket-mode: 0666
  1049  `, &snap.SideInfo{Revision: snap.R(12)})
  1050  
  1051  	calls := 0
  1052  	r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1053  		if len(cmd) == 1 && cmd[0] == "daemon-reload" && calls == 0 {
  1054  			// only fail the first systemd daemon-reload call, the
  1055  			// second one is at the end of cleanup
  1056  			calls += 1
  1057  			return nil, fmt.Errorf("failed")
  1058  		}
  1059  		return []byte("ActiveState=inactive\n"), nil
  1060  	})
  1061  	defer r()
  1062  
  1063  	err := wrappers.AddSnapServices(info, &progress.Null)
  1064  	c.Assert(err, NotNil)
  1065  
  1066  	c.Logf("services dir: %v", dirs.SnapServicesDir)
  1067  	matches, err := filepath.Glob(dirs.SnapServicesDir + "/*")
  1068  	c.Assert(err, IsNil)
  1069  	c.Assert(matches, HasLen, 0, Commentf("the following autogenerated files were left behind: %v", matches))
  1070  }
  1071  
  1072  func (s *servicesTestSuite) TestAddServicesDidReload(c *C) {
  1073  	const base = `name: hello-snap
  1074  version: 1.10
  1075  summary: hello
  1076  description: Hello...
  1077  apps:
  1078  `
  1079  	onlyServices := snaptest.MockSnap(c, base+`
  1080   svc1:
  1081    command: bin/hello
  1082    daemon: simple
  1083  `, &snap.SideInfo{Revision: snap.R(12)})
  1084  
  1085  	onlySockets := snaptest.MockSnap(c, base+`
  1086   svc1:
  1087    command: bin/hello
  1088    daemon: simple
  1089    plugs: [network-bind]
  1090    sockets:
  1091      sock1:
  1092        listen-stream: $SNAP_COMMON/sock1.socket
  1093        socket-mode: 0666
  1094  `, &snap.SideInfo{Revision: snap.R(12)})
  1095  
  1096  	onlyTimers := snaptest.MockSnap(c, base+`
  1097   svc1:
  1098    command: bin/hello
  1099    daemon: oneshot
  1100    timer: 10:00-12:00
  1101  `, &snap.SideInfo{Revision: snap.R(12)})
  1102  
  1103  	for i, info := range []*snap.Info{onlyServices, onlySockets, onlyTimers} {
  1104  		s.sysdLog = nil
  1105  		err := wrappers.AddSnapServices(info, &progress.Null)
  1106  		c.Assert(err, IsNil)
  1107  		reloads := 0
  1108  		c.Logf("calls: %v", s.sysdLog)
  1109  		for _, call := range s.sysdLog {
  1110  			if strutil.ListContains(call, "daemon-reload") {
  1111  				reloads += 1
  1112  			}
  1113  		}
  1114  		c.Check(reloads >= 1, Equals, true, Commentf("test-case %v did not reload services as expected", i))
  1115  	}
  1116  }
  1117  
  1118  func (s *servicesTestSuite) TestSnapServicesActivation(c *C) {
  1119  	const snapYaml = `name: hello-snap
  1120  version: 1.10
  1121  summary: hello
  1122  description: Hello...
  1123  apps:
  1124   svc1:
  1125    command: bin/hello
  1126    daemon: simple
  1127    plugs: [network-bind]
  1128    sockets:
  1129      sock1:
  1130        listen-stream: $SNAP_COMMON/sock1.socket
  1131        socket-mode: 0666
  1132   svc2:
  1133    command: bin/hello
  1134    daemon: oneshot
  1135    timer: 10:00-12:00
  1136   svc3:
  1137    command: bin/hello
  1138    daemon: simple
  1139  `
  1140  
  1141  	svc3Name := "snap.hello-snap.svc3.service"
  1142  
  1143  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
  1144  
  1145  	// fix the apps order to make the test stable
  1146  	err := wrappers.AddSnapServices(info, nil)
  1147  	c.Assert(err, IsNil)
  1148  	c.Assert(s.sysdLog, HasLen, 2, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog))
  1149  	c.Check(s.sysdLog, DeepEquals, [][]string{
  1150  		// only svc3 gets started during boot
  1151  		{"--root", dirs.GlobalRootDir, "enable", svc3Name},
  1152  		{"daemon-reload"},
  1153  	}, Commentf("calls: %v", s.sysdLog))
  1154  }
  1155  
  1156  func (s *servicesTestSuite) TestServiceRestartDelay(c *C) {
  1157  	snapYaml := packageHello + `
  1158   svc2:
  1159     daemon: forking
  1160     restart-delay: 12s
  1161   svc3:
  1162     daemon: forking
  1163  `
  1164  	info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)})
  1165  
  1166  	err := wrappers.AddSnapServices(info, nil)
  1167  	c.Assert(err, IsNil)
  1168  
  1169  	content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"))
  1170  	c.Assert(err, IsNil)
  1171  	c.Check(strings.Contains(string(content), "\nRestartSec=12\n"), Equals, true)
  1172  
  1173  	content, err = ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"))
  1174  	c.Assert(err, IsNil)
  1175  	c.Check(strings.Contains(string(content), "RestartSec="), Equals, false)
  1176  }