github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/servicestate/service_control_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 servicestate_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"sort"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	. "gopkg.in/check.v1"
    32  
    33  	"github.com/snapcore/snapd/client"
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/overlord"
    36  	"github.com/snapcore/snapd/overlord/servicestate"
    37  	"github.com/snapcore/snapd/overlord/snapstate"
    38  	"github.com/snapcore/snapd/overlord/state"
    39  	"github.com/snapcore/snapd/snap"
    40  	"github.com/snapcore/snapd/snap/snaptest"
    41  	"github.com/snapcore/snapd/systemd"
    42  	"github.com/snapcore/snapd/systemd/systemdtest"
    43  	"github.com/snapcore/snapd/testutil"
    44  )
    45  
    46  func TestServiceControl(t *testing.T) { TestingT(t) }
    47  
    48  type serviceControlSuite struct {
    49  	testutil.BaseTest
    50  	state      *state.State
    51  	o          *overlord.Overlord
    52  	se         *overlord.StateEngine
    53  	serviceMgr *servicestate.ServiceManager
    54  	sysctlArgs [][]string
    55  }
    56  
    57  var _ = Suite(&serviceControlSuite{})
    58  
    59  const servicesSnapYaml1 = `name: test-snap
    60  version: 1.0
    61  apps:
    62    someapp:
    63      command: cmd
    64    abc:
    65      daemon: simple
    66      after: [bar]
    67    foo:
    68      daemon: simple
    69    bar:
    70      daemon: simple
    71      after: [foo]
    72  `
    73  
    74  func (s *serviceControlSuite) SetUpTest(c *C) {
    75  	s.BaseTest.SetUpTest(c)
    76  
    77  	dirs.SetRootDir(c.MkDir())
    78  	s.AddCleanup(func() { dirs.SetRootDir("") })
    79  
    80  	s.o = overlord.Mock()
    81  	s.state = s.o.State()
    82  
    83  	s.sysctlArgs = nil
    84  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) (buf []byte, err error) {
    85  		// When calling the "snap restart" command, the initial status will be
    86  		// retrieved first. Services which are not running will be
    87  		// ignored. Therefore, mock this "show" operation by pretending that
    88  		// all requested services are active:
    89  		if out := systemdtest.HandleMockAllUnitsActiveOutput(cmd, nil); out != nil {
    90  			return out, nil
    91  		}
    92  
    93  		s.sysctlArgs = append(s.sysctlArgs, cmd)
    94  		if cmd[0] == "show" {
    95  			return []byte("ActiveState=inactive\n"), nil
    96  		}
    97  		return nil, nil
    98  	})
    99  	s.AddCleanup(systemctlRestorer)
   100  
   101  	s.serviceMgr = servicestate.Manager(s.state, s.o.TaskRunner())
   102  	s.o.AddManager(s.serviceMgr)
   103  	s.o.AddManager(s.o.TaskRunner())
   104  	s.se = s.o.StateEngine()
   105  	c.Assert(s.o.StartUp(), IsNil)
   106  }
   107  
   108  func (s *serviceControlSuite) mockTestSnap(c *C) *snap.Info {
   109  	si := snap.SideInfo{
   110  		RealName: "test-snap",
   111  		Revision: snap.R(7),
   112  	}
   113  	info := snaptest.MockSnap(c, servicesSnapYaml1, &si)
   114  	snapstate.Set(s.state, "test-snap", &snapstate.SnapState{
   115  		Active:   true,
   116  		Sequence: []*snap.SideInfo{&si},
   117  		Current:  snap.R(7),
   118  		SnapType: "app",
   119  	})
   120  
   121  	// mock systemd service units, this is required when testing "stop"
   122  	err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc/systemd/system/"), 0775)
   123  	c.Assert(err, IsNil)
   124  	err = ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "etc/systemd/system/snap.test-snap.foo.service"), nil, 0644)
   125  	c.Assert(err, IsNil)
   126  
   127  	return info
   128  }
   129  
   130  func verifyUnsortedInvocations(c *C, sysctlArgs [][]string, action string,
   131  	expectedArguments []string) {
   132  	/* We don't care about the order of the invocations, as long as
   133  	 * they all carry the same action */
   134  	arguments := []string{}
   135  	for _, params := range sysctlArgs {
   136  		c.Check(params[0], Equals, action)
   137  		arguments = append(arguments, params[1])
   138  	}
   139  	sort.Strings(arguments)
   140  	c.Check(arguments, DeepEquals, expectedArguments)
   141  }
   142  
   143  func verifyControlTasks(c *C, tasks []*state.Task, expectedAction, actionModifier string,
   144  	expectedServices []string, expectedExplicitServices []string) {
   145  	// sanity, ensures test checks below are hit
   146  	c.Assert(len(tasks) > 0, Equals, true)
   147  
   148  	// group service names by snaps
   149  	bySnap := make(map[string][]string)
   150  	for _, name := range expectedServices {
   151  		// split service name, e.g. snap.test-snap.foo.service
   152  		parts := strings.Split(name, ".")
   153  		c.Assert(parts, HasLen, 4)
   154  		snapName := parts[1]
   155  		serviceName := parts[2]
   156  		bySnap[snapName] = append(bySnap[snapName], serviceName)
   157  	}
   158  
   159  	var execCommandTasks int
   160  	var serviceControlTasks int
   161  	// snaps from service-control tasks
   162  	seenSnaps := make(map[string]bool)
   163  
   164  	var i int
   165  	for i < len(tasks) {
   166  		var argv []string
   167  		kind := tasks[i].Kind()
   168  		if kind == "exec-command" {
   169  			execCommandTasks++
   170  			var ignore bool
   171  			c.Assert(tasks[i].Get("ignore", &ignore), IsNil)
   172  			c.Check(ignore, Equals, true)
   173  			switch expectedAction {
   174  			case "start":
   175  				if actionModifier != "" {
   176  					c.Assert(tasks[i].Get("argv", &argv), IsNil)
   177  					c.Check(argv, DeepEquals, append([]string{"systemctl", actionModifier}, expectedServices...))
   178  					i++
   179  					wt := tasks[i].WaitTasks()
   180  					c.Assert(wt, HasLen, 1)
   181  					c.Assert(wt[0].ID(), Equals, tasks[i-1].ID())
   182  				}
   183  				c.Assert(tasks[i].Get("argv", &argv), IsNil)
   184  				c.Check(argv, DeepEquals, append([]string{"systemctl", "start"}, expectedServices...))
   185  			case "stop":
   186  				if actionModifier != "" {
   187  					c.Assert(tasks[i].Get("argv", &argv), IsNil)
   188  					c.Check(argv, DeepEquals, append([]string{"systemctl", actionModifier}, expectedServices...))
   189  					i++
   190  					wt := tasks[i].WaitTasks()
   191  					c.Assert(wt, HasLen, 1)
   192  					c.Assert(wt[0].ID(), Equals, tasks[i-1].ID())
   193  				}
   194  				c.Assert(tasks[i].Get("argv", &argv), IsNil)
   195  				c.Check(argv, DeepEquals, append([]string{"systemctl", "stop"}, expectedServices...))
   196  			case "restart":
   197  				if actionModifier != "" {
   198  					c.Assert(tasks[i].Get("argv", &argv), IsNil)
   199  					c.Check(argv, DeepEquals, append([]string{"systemctl", "reload-or-restart"}, expectedServices...))
   200  				} else {
   201  					c.Assert(tasks[i].Get("argv", &argv), IsNil)
   202  					c.Check(argv, DeepEquals, append([]string{"systemctl", "restart"}, expectedServices...))
   203  				}
   204  			default:
   205  				c.Fatalf("unhandled action %s", expectedAction)
   206  			}
   207  		} else if kind == "service-control" {
   208  			serviceControlTasks++
   209  			var sa servicestate.ServiceAction
   210  			c.Assert(tasks[i].Get("service-action", &sa), IsNil)
   211  			switch expectedAction {
   212  			case "start":
   213  				c.Check(sa.Action, Equals, "start")
   214  				if actionModifier != "" {
   215  					c.Check(sa.ActionModifier, Equals, actionModifier)
   216  				}
   217  			case "stop":
   218  				c.Check(sa.Action, Equals, "stop")
   219  				if actionModifier != "" {
   220  					c.Check(sa.ActionModifier, Equals, actionModifier)
   221  				}
   222  			case "restart":
   223  				if actionModifier == "reload" {
   224  					c.Check(sa.Action, Equals, "reload-or-restart")
   225  				} else {
   226  					c.Check(sa.Action, Equals, "restart")
   227  				}
   228  			default:
   229  				c.Fatalf("unhandled action %s", expectedAction)
   230  			}
   231  			seenSnaps[sa.SnapName] = true
   232  			obtainedServices := sa.Services
   233  			sort.Strings(obtainedServices)
   234  			sort.Strings(bySnap[sa.SnapName])
   235  			c.Assert(obtainedServices, DeepEquals, bySnap[sa.SnapName])
   236  			c.Assert(sa.ExplicitServices, DeepEquals, expectedExplicitServices)
   237  		} else {
   238  			c.Fatalf("unexpected task: %s", tasks[i].Kind())
   239  		}
   240  		i++
   241  	}
   242  
   243  	c.Check(execCommandTasks > 0, Equals, true)
   244  
   245  	// we should have one service-control task for every snap
   246  	c.Assert(serviceControlTasks, Equals, len(bySnap))
   247  	c.Assert(len(bySnap), Equals, len(seenSnaps))
   248  	for snapName := range bySnap {
   249  		c.Assert(seenSnaps[snapName], Equals, true)
   250  	}
   251  }
   252  
   253  func makeControlChange(c *C, st *state.State, inst *servicestate.Instruction, info *snap.Info, appNames []string) *state.Change {
   254  	apps := []*snap.AppInfo{}
   255  	for _, name := range appNames {
   256  		c.Assert(info.Apps[name], NotNil)
   257  		apps = append(apps, info.Apps[name])
   258  	}
   259  
   260  	flags := &servicestate.Flags{CreateExecCommandTasks: true}
   261  	tss, err := servicestate.Control(st, apps, inst, flags, nil)
   262  	c.Assert(err, IsNil)
   263  
   264  	chg := st.NewChange("service-control", "...")
   265  	for _, ts := range tss {
   266  		chg.AddAll(ts)
   267  	}
   268  	return chg
   269  }
   270  
   271  func (s *serviceControlSuite) TestControlDoesntCreateExecCommandTasksIfNoFlags(c *C) {
   272  	st := s.state
   273  	st.Lock()
   274  	defer st.Unlock()
   275  
   276  	info := s.mockTestSnap(c)
   277  	inst := &servicestate.Instruction{
   278  		Action: "start",
   279  		Names:  []string{"foo"},
   280  	}
   281  
   282  	flags := &servicestate.Flags{}
   283  	tss, err := servicestate.Control(st, []*snap.AppInfo{info.Apps["foo"]}, inst, flags, nil)
   284  	c.Assert(err, IsNil)
   285  	// service-control is the only task
   286  	c.Assert(tss, HasLen, 1)
   287  	c.Assert(tss[0].Tasks(), HasLen, 1)
   288  	c.Check(tss[0].Tasks()[0].Kind(), Equals, "service-control")
   289  }
   290  
   291  func (s *serviceControlSuite) TestControlConflict(c *C) {
   292  	st := s.state
   293  	st.Lock()
   294  	defer st.Unlock()
   295  
   296  	inf := s.mockTestSnap(c)
   297  
   298  	// create conflicting change
   299  	t := st.NewTask("link-snap", "...")
   300  	snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "test-snap"}}
   301  	t.Set("snap-setup", snapsup)
   302  	chg := st.NewChange("manip", "...")
   303  	chg.AddTask(t)
   304  
   305  	inst := &servicestate.Instruction{Action: "start", Names: []string{"foo"}}
   306  	_, err := servicestate.Control(st, []*snap.AppInfo{inf.Apps["foo"]}, inst, nil, nil)
   307  	c.Check(err, ErrorMatches, `snap "test-snap" has "manip" change in progress`)
   308  }
   309  
   310  func (s *serviceControlSuite) TestControlStartInstruction(c *C) {
   311  	st := s.state
   312  	st.Lock()
   313  	defer st.Unlock()
   314  
   315  	inf := s.mockTestSnap(c)
   316  
   317  	inst := &servicestate.Instruction{
   318  		Action: "start",
   319  	}
   320  
   321  	chg := makeControlChange(c, st, inst, inf, []string{"foo"})
   322  	verifyControlTasks(c, chg.Tasks(), "start", "",
   323  		[]string{"snap.test-snap.foo.service"}, nil)
   324  }
   325  
   326  func (s *serviceControlSuite) TestControlStartEnableMultipleInstruction(c *C) {
   327  	st := s.state
   328  	st.Lock()
   329  	defer st.Unlock()
   330  
   331  	inf := s.mockTestSnap(c)
   332  
   333  	inst := &servicestate.Instruction{
   334  		Action:       "start",
   335  		StartOptions: client.StartOptions{Enable: true},
   336  	}
   337  
   338  	chg := makeControlChange(c, st, inst, inf, []string{"foo", "bar"})
   339  	verifyControlTasks(c, chg.Tasks(), "start", "enable",
   340  		[]string{"snap.test-snap.foo.service", "snap.test-snap.bar.service"}, nil)
   341  }
   342  
   343  func (s *serviceControlSuite) TestControlStopInstruction(c *C) {
   344  	st := s.state
   345  	st.Lock()
   346  	defer st.Unlock()
   347  
   348  	inf := s.mockTestSnap(c)
   349  
   350  	inst := &servicestate.Instruction{
   351  		Action: "stop",
   352  	}
   353  
   354  	chg := makeControlChange(c, st, inst, inf, []string{"foo"})
   355  	verifyControlTasks(c, chg.Tasks(), "stop", "",
   356  		[]string{"snap.test-snap.foo.service"}, nil)
   357  }
   358  
   359  func (s *serviceControlSuite) TestControlStopDisableInstruction(c *C) {
   360  	st := s.state
   361  	st.Lock()
   362  	defer st.Unlock()
   363  
   364  	inf := s.mockTestSnap(c)
   365  
   366  	inst := &servicestate.Instruction{
   367  		Action:      "stop",
   368  		StopOptions: client.StopOptions{Disable: true},
   369  	}
   370  
   371  	chg := makeControlChange(c, st, inst, inf, []string{"bar"})
   372  	verifyControlTasks(c, chg.Tasks(), "stop", "disable",
   373  		[]string{"snap.test-snap.bar.service"}, nil)
   374  }
   375  
   376  func (s *serviceControlSuite) TestControlRestartInstruction(c *C) {
   377  	st := s.state
   378  	st.Lock()
   379  	defer st.Unlock()
   380  
   381  	inf := s.mockTestSnap(c)
   382  
   383  	inst := &servicestate.Instruction{
   384  		Action: "restart",
   385  	}
   386  
   387  	chg := makeControlChange(c, st, inst, inf, []string{"bar"})
   388  	verifyControlTasks(c, chg.Tasks(), "restart", "",
   389  		[]string{"snap.test-snap.bar.service"}, nil)
   390  }
   391  
   392  func (s *serviceControlSuite) TestControlRestartReloadMultipleInstruction(c *C) {
   393  	st := s.state
   394  	st.Lock()
   395  	defer st.Unlock()
   396  
   397  	inf := s.mockTestSnap(c)
   398  
   399  	inst := &servicestate.Instruction{
   400  		Action:         "restart",
   401  		RestartOptions: client.RestartOptions{Reload: true},
   402  	}
   403  
   404  	chg := makeControlChange(c, st, inst, inf, []string{"foo", "bar"})
   405  	verifyControlTasks(c, chg.Tasks(), "restart", "reload",
   406  		[]string{"snap.test-snap.foo.service", "snap.test-snap.bar.service"},
   407  		nil)
   408  }
   409  
   410  func (s *serviceControlSuite) TestControlUnknownInstruction(c *C) {
   411  	st := s.state
   412  	st.Lock()
   413  	defer st.Unlock()
   414  
   415  	info := s.mockTestSnap(c)
   416  	inst := &servicestate.Instruction{
   417  		Action:         "boo",
   418  		RestartOptions: client.RestartOptions{Reload: true},
   419  	}
   420  
   421  	_, err := servicestate.Control(st, []*snap.AppInfo{info.Apps["foo"]}, inst, nil, nil)
   422  	c.Assert(err, ErrorMatches, `unknown action "boo"`)
   423  }
   424  
   425  func (s *serviceControlSuite) TestControlStopDisableMultipleInstruction(c *C) {
   426  	st := s.state
   427  	st.Lock()
   428  	defer st.Unlock()
   429  
   430  	inf := s.mockTestSnap(c)
   431  
   432  	inst := &servicestate.Instruction{
   433  		Action:      "stop",
   434  		StopOptions: client.StopOptions{Disable: true},
   435  	}
   436  
   437  	chg := makeControlChange(c, st, inst, inf, []string{"foo", "bar"})
   438  	verifyControlTasks(c, chg.Tasks(), "stop", "disable",
   439  		[]string{"snap.test-snap.foo.service", "snap.test-snap.bar.service"},
   440  		nil)
   441  }
   442  
   443  func (s *serviceControlSuite) TestWithExplicitServices(c *C) {
   444  	st := s.state
   445  	st.Lock()
   446  	defer st.Unlock()
   447  
   448  	inf := s.mockTestSnap(c)
   449  
   450  	inst := &servicestate.Instruction{
   451  		Action: "start",
   452  		Names:  []string{"test-snap.foo", "test-snap.abc"},
   453  	}
   454  
   455  	chg := makeControlChange(c, st, inst, inf, []string{"abc", "foo", "bar"})
   456  	verifyControlTasks(c, chg.Tasks(), "start", "",
   457  		[]string{"snap.test-snap.abc.service", "snap.test-snap.foo.service", "snap.test-snap.bar.service"},
   458  		[]string{"snap.test-snap.abc.service", "snap.test-snap.foo.service"})
   459  }
   460  
   461  func (s *serviceControlSuite) TestWithExplicitServicesAndSnapName(c *C) {
   462  	st := s.state
   463  	st.Lock()
   464  	defer st.Unlock()
   465  
   466  	inf := s.mockTestSnap(c)
   467  
   468  	inst := &servicestate.Instruction{
   469  		Action: "start",
   470  		Names:  []string{"test-snap.foo", "test-snap", "test-snap.bar"},
   471  	}
   472  
   473  	chg := makeControlChange(c, st, inst, inf, []string{"abc", "foo", "bar"})
   474  	verifyControlTasks(c, chg.Tasks(), "start", "",
   475  		[]string{"snap.test-snap.abc.service", "snap.test-snap.foo.service", "snap.test-snap.bar.service"},
   476  		[]string{"snap.test-snap.bar.service", "snap.test-snap.foo.service"})
   477  }
   478  
   479  func (s *serviceControlSuite) TestNoServiceCommandError(c *C) {
   480  	st := s.state
   481  	st.Lock()
   482  	defer st.Unlock()
   483  
   484  	chg := st.NewChange("service-control", "...")
   485  	t := st.NewTask("service-control", "...")
   486  	chg.AddTask(t)
   487  
   488  	st.Unlock()
   489  	defer s.se.Stop()
   490  	err := s.o.Settle(5 * time.Second)
   491  	st.Lock()
   492  	c.Assert(err, IsNil)
   493  
   494  	c.Assert(t.Status(), Equals, state.ErrorStatus)
   495  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*internal error: cannot get service-action: no state entry for key.*`)
   496  }
   497  
   498  func (s *serviceControlSuite) TestNoopWhenNoServices(c *C) {
   499  	st := s.state
   500  	st.Lock()
   501  	defer st.Unlock()
   502  
   503  	si := snap.SideInfo{RealName: "test-snap", Revision: snap.R(7)}
   504  	snaptest.MockSnap(c, `name: test-snap`, &si)
   505  	snapstate.Set(st, "test-snap", &snapstate.SnapState{
   506  		Active:   true,
   507  		Sequence: []*snap.SideInfo{&si},
   508  		Current:  snap.R(7),
   509  		SnapType: "app",
   510  	})
   511  
   512  	chg := st.NewChange("service-control", "...")
   513  	t := st.NewTask("service-control", "...")
   514  	cmd := &servicestate.ServiceAction{SnapName: "test-snap"}
   515  	t.Set("service-action", cmd)
   516  	chg.AddTask(t)
   517  
   518  	st.Unlock()
   519  	defer s.se.Stop()
   520  	err := s.o.Settle(5 * time.Second)
   521  	st.Lock()
   522  	c.Assert(err, IsNil)
   523  
   524  	c.Check(t.Status(), Equals, state.DoneStatus)
   525  }
   526  
   527  func (s *serviceControlSuite) TestUnhandledServiceAction(c *C) {
   528  	st := s.state
   529  	st.Lock()
   530  	defer st.Unlock()
   531  
   532  	s.mockTestSnap(c)
   533  
   534  	chg := st.NewChange("service-control", "...")
   535  	t := st.NewTask("service-control", "...")
   536  	cmd := &servicestate.ServiceAction{SnapName: "test-snap", Action: "foo"}
   537  	t.Set("service-action", cmd)
   538  	chg.AddTask(t)
   539  
   540  	st.Unlock()
   541  	defer s.se.Stop()
   542  	err := s.o.Settle(5 * time.Second)
   543  	st.Lock()
   544  	c.Assert(err, IsNil)
   545  
   546  	c.Assert(t.Status(), Equals, state.ErrorStatus)
   547  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*unhandled service action: "foo".*`)
   548  }
   549  
   550  func (s *serviceControlSuite) TestUnknownService(c *C) {
   551  	st := s.state
   552  	st.Lock()
   553  	defer st.Unlock()
   554  
   555  	s.mockTestSnap(c)
   556  
   557  	chg := st.NewChange("service-control", "...")
   558  	t := st.NewTask("service-control", "...")
   559  	cmd := &servicestate.ServiceAction{
   560  		SnapName: "test-snap",
   561  		Action:   "start",
   562  		Services: []string{"baz"},
   563  	}
   564  	t.Set("service-action", cmd)
   565  	chg.AddTask(t)
   566  
   567  	st.Unlock()
   568  	defer s.se.Stop()
   569  	err := s.o.Settle(5 * time.Second)
   570  	st.Lock()
   571  	c.Assert(err, IsNil)
   572  
   573  	c.Assert(t.Status(), Equals, state.ErrorStatus)
   574  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*no such service: baz.*`)
   575  }
   576  
   577  func (s *serviceControlSuite) TestNotAService(c *C) {
   578  	st := s.state
   579  	st.Lock()
   580  	defer st.Unlock()
   581  
   582  	s.mockTestSnap(c)
   583  
   584  	chg := st.NewChange("service-control", "...")
   585  	t := st.NewTask("service-control", "...")
   586  	cmd := &servicestate.ServiceAction{
   587  		SnapName: "test-snap",
   588  		Action:   "start",
   589  		Services: []string{"someapp"},
   590  	}
   591  	t.Set("service-action", cmd)
   592  	chg.AddTask(t)
   593  
   594  	st.Unlock()
   595  	defer s.se.Stop()
   596  	err := s.o.Settle(5 * time.Second)
   597  	st.Lock()
   598  	c.Assert(err, IsNil)
   599  
   600  	c.Assert(t.Status(), Equals, state.ErrorStatus)
   601  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*someapp is not a service.*`)
   602  }
   603  
   604  func (s *serviceControlSuite) TestStartAllServices(c *C) {
   605  	st := s.state
   606  	st.Lock()
   607  	defer st.Unlock()
   608  
   609  	s.mockTestSnap(c)
   610  
   611  	chg := st.NewChange("service-control", "...")
   612  	t := st.NewTask("service-control", "...")
   613  	cmd := &servicestate.ServiceAction{
   614  		SnapName: "test-snap",
   615  		Action:   "start",
   616  	}
   617  	t.Set("service-action", cmd)
   618  	chg.AddTask(t)
   619  
   620  	st.Unlock()
   621  	defer s.se.Stop()
   622  	err := s.o.Settle(5 * time.Second)
   623  	st.Lock()
   624  	c.Assert(err, IsNil)
   625  
   626  	c.Assert(t.Status(), Equals, state.DoneStatus)
   627  
   628  	/* We don't care about the order of the is-enabled invocations, as long as
   629  	 * they all happen before the first invocation of "start" */
   630  	verifyUnsortedInvocations(c, s.sysctlArgs[:3], "is-enabled", []string{
   631  		"snap.test-snap.abc.service",
   632  		"snap.test-snap.bar.service",
   633  		"snap.test-snap.foo.service",
   634  	})
   635  	c.Check(s.sysctlArgs[3:], DeepEquals, [][]string{
   636  		{"start", "snap.test-snap.foo.service"},
   637  		{"start", "snap.test-snap.bar.service"},
   638  		{"start", "snap.test-snap.abc.service"},
   639  	})
   640  }
   641  
   642  func (s *serviceControlSuite) TestStartListedServices(c *C) {
   643  	st := s.state
   644  	st.Lock()
   645  	defer st.Unlock()
   646  
   647  	s.mockTestSnap(c)
   648  
   649  	chg := st.NewChange("service-control", "...")
   650  	t := st.NewTask("service-control", "...")
   651  	cmd := &servicestate.ServiceAction{
   652  		SnapName: "test-snap",
   653  		Action:   "start",
   654  		Services: []string{"foo"},
   655  	}
   656  	t.Set("service-action", cmd)
   657  	chg.AddTask(t)
   658  
   659  	st.Unlock()
   660  	defer s.se.Stop()
   661  	err := s.o.Settle(5 * time.Second)
   662  	st.Lock()
   663  	c.Assert(err, IsNil)
   664  
   665  	c.Assert(t.Status(), Equals, state.DoneStatus)
   666  	verifyUnsortedInvocations(c, s.sysctlArgs[:3], "is-enabled", []string{
   667  		"snap.test-snap.abc.service",
   668  		"snap.test-snap.bar.service",
   669  		"snap.test-snap.foo.service",
   670  	})
   671  	c.Check(s.sysctlArgs[3:], DeepEquals, [][]string{
   672  		{"start", "snap.test-snap.foo.service"},
   673  	})
   674  }
   675  
   676  func (s *serviceControlSuite) TestStartEnableServices(c *C) {
   677  	st := s.state
   678  	st.Lock()
   679  	defer st.Unlock()
   680  
   681  	s.mockTestSnap(c)
   682  
   683  	chg := st.NewChange("service-control", "...")
   684  	t := st.NewTask("service-control", "...")
   685  	cmd := &servicestate.ServiceAction{
   686  		SnapName:       "test-snap",
   687  		Action:         "start",
   688  		ActionModifier: "enable",
   689  		Services:       []string{"foo"},
   690  	}
   691  	t.Set("service-action", cmd)
   692  	chg.AddTask(t)
   693  
   694  	st.Unlock()
   695  	defer s.se.Stop()
   696  	err := s.o.Settle(5 * time.Second)
   697  	st.Lock()
   698  	c.Assert(err, IsNil)
   699  
   700  	c.Assert(t.Status(), Equals, state.DoneStatus)
   701  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   702  		{"enable", "snap.test-snap.foo.service"},
   703  		{"start", "snap.test-snap.foo.service"},
   704  	})
   705  }
   706  
   707  func (s *serviceControlSuite) TestStartEnableMultipleServices(c *C) {
   708  	st := s.state
   709  	st.Lock()
   710  	defer st.Unlock()
   711  
   712  	s.mockTestSnap(c)
   713  
   714  	chg := st.NewChange("service-control", "...")
   715  	t := st.NewTask("service-control", "...")
   716  	cmd := &servicestate.ServiceAction{
   717  		SnapName:       "test-snap",
   718  		Action:         "start",
   719  		ActionModifier: "enable",
   720  	}
   721  	t.Set("service-action", cmd)
   722  	chg.AddTask(t)
   723  
   724  	st.Unlock()
   725  	defer s.se.Stop()
   726  	err := s.o.Settle(5 * time.Second)
   727  	st.Lock()
   728  	c.Assert(err, IsNil)
   729  
   730  	c.Assert(t.Status(), Equals, state.DoneStatus)
   731  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   732  		{"enable", "snap.test-snap.foo.service"},
   733  		{"enable", "snap.test-snap.bar.service"},
   734  		{"enable", "snap.test-snap.abc.service"},
   735  		{"start", "snap.test-snap.foo.service"},
   736  		{"start", "snap.test-snap.bar.service"},
   737  		{"start", "snap.test-snap.abc.service"},
   738  	})
   739  }
   740  
   741  func (s *serviceControlSuite) TestStopServices(c *C) {
   742  	st := s.state
   743  	st.Lock()
   744  	defer st.Unlock()
   745  
   746  	s.mockTestSnap(c)
   747  
   748  	chg := st.NewChange("service-control", "...")
   749  	t := st.NewTask("service-control", "...")
   750  	cmd := &servicestate.ServiceAction{
   751  		SnapName: "test-snap",
   752  		Action:   "stop",
   753  		Services: []string{"foo"},
   754  	}
   755  	t.Set("service-action", cmd)
   756  	chg.AddTask(t)
   757  
   758  	st.Unlock()
   759  	defer s.se.Stop()
   760  	err := s.o.Settle(5 * time.Second)
   761  	st.Lock()
   762  	c.Assert(err, IsNil)
   763  
   764  	c.Assert(t.Status(), Equals, state.DoneStatus)
   765  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   766  		{"stop", "snap.test-snap.foo.service"},
   767  		{"show", "--property=ActiveState", "snap.test-snap.foo.service"},
   768  	})
   769  }
   770  
   771  func (s *serviceControlSuite) TestStopDisableServices(c *C) {
   772  	st := s.state
   773  	st.Lock()
   774  	defer st.Unlock()
   775  
   776  	s.mockTestSnap(c)
   777  
   778  	chg := st.NewChange("service-control", "...")
   779  	t := st.NewTask("service-control", "...")
   780  	cmd := &servicestate.ServiceAction{
   781  		SnapName:       "test-snap",
   782  		Action:         "stop",
   783  		ActionModifier: "disable",
   784  		Services:       []string{"foo"},
   785  	}
   786  	t.Set("service-action", cmd)
   787  	chg.AddTask(t)
   788  
   789  	st.Unlock()
   790  	defer s.se.Stop()
   791  	err := s.o.Settle(5 * time.Second)
   792  	st.Lock()
   793  	c.Assert(err, IsNil)
   794  
   795  	c.Assert(t.Status(), Equals, state.DoneStatus)
   796  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   797  		{"stop", "snap.test-snap.foo.service"},
   798  		{"show", "--property=ActiveState", "snap.test-snap.foo.service"},
   799  		{"disable", "snap.test-snap.foo.service"},
   800  	})
   801  }
   802  
   803  func (s *serviceControlSuite) TestRestartServices(c *C) {
   804  	st := s.state
   805  	st.Lock()
   806  	defer st.Unlock()
   807  
   808  	s.mockTestSnap(c)
   809  
   810  	chg := st.NewChange("service-control", "...")
   811  	t := st.NewTask("service-control", "...")
   812  	cmd := &servicestate.ServiceAction{
   813  		SnapName: "test-snap",
   814  		Action:   "restart",
   815  		Services: []string{"foo"},
   816  	}
   817  	t.Set("service-action", cmd)
   818  	chg.AddTask(t)
   819  
   820  	st.Unlock()
   821  	defer s.se.Stop()
   822  	err := s.o.Settle(5 * time.Second)
   823  	st.Lock()
   824  	c.Assert(err, IsNil)
   825  
   826  	c.Assert(t.Status(), Equals, state.DoneStatus)
   827  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   828  		{"stop", "snap.test-snap.foo.service"},
   829  		{"show", "--property=ActiveState", "snap.test-snap.foo.service"},
   830  		{"start", "snap.test-snap.foo.service"},
   831  	})
   832  }
   833  
   834  func (s *serviceControlSuite) testRestartWithExplicitServicesCommon(c *C,
   835  	explicitServices []string, expectedInvocations [][]string) {
   836  	st := s.state
   837  	st.Lock()
   838  	defer st.Unlock()
   839  
   840  	s.mockTestSnap(c)
   841  
   842  	srvAbc := "snap.test-snap.abc.service"
   843  	srvFoo := "snap.test-snap.foo.service"
   844  	srvBar := "snap.test-snap.bar.service"
   845  
   846  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) (buf []byte, err error) {
   847  		states := map[string]systemdtest.ServiceState{
   848  			srvAbc: {ActiveState: "inactive", UnitFileState: "enabled"},
   849  			srvFoo: {ActiveState: "inactive", UnitFileState: "enabled"},
   850  			srvBar: {ActiveState: "active", UnitFileState: "enabled"},
   851  		}
   852  		if out := systemdtest.HandleMockAllUnitsActiveOutput(cmd, states); out != nil {
   853  			return out, nil
   854  		}
   855  
   856  		s.sysctlArgs = append(s.sysctlArgs, cmd)
   857  		if cmd[0] == "show" {
   858  			return []byte("ActiveState=inactive\n"), nil
   859  		}
   860  		return nil, nil
   861  	})
   862  	s.AddCleanup(systemctlRestorer)
   863  
   864  	chg := st.NewChange("service-control", "...")
   865  	t := st.NewTask("service-control", "...")
   866  	cmd := &servicestate.ServiceAction{
   867  		SnapName: "test-snap",
   868  		Action:   "restart",
   869  		Services: []string{"abc", "foo", "bar"},
   870  		// these services will be restarted even if inactive
   871  		ExplicitServices: explicitServices,
   872  	}
   873  	t.Set("service-action", cmd)
   874  	chg.AddTask(t)
   875  
   876  	st.Unlock()
   877  	defer s.se.Stop()
   878  	err := s.o.Settle(5 * time.Second)
   879  	st.Lock()
   880  	c.Assert(err, IsNil)
   881  
   882  	c.Assert(t.Status(), Equals, state.DoneStatus)
   883  	// We expect to get foo and bar restarted: "bar" because it was already
   884  	// running and "foo" because it was explicitly mentioned (despite being
   885  	// inactive). "abc" was not running and not explicitly mentioned, so it
   886  	// shouldn't get restarted.
   887  	c.Check(s.sysctlArgs, DeepEquals, expectedInvocations)
   888  }
   889  
   890  func (s *serviceControlSuite) TestRestartWithSomeExplicitServices(c *C) {
   891  	srvFoo := "snap.test-snap.foo.service"
   892  	srvBar := "snap.test-snap.bar.service"
   893  	s.testRestartWithExplicitServicesCommon(c,
   894  		[]string{srvFoo},
   895  		[][]string{
   896  			{"stop", srvFoo},
   897  			{"show", "--property=ActiveState", srvFoo},
   898  			{"start", srvFoo},
   899  			{"stop", srvBar},
   900  			{"show", "--property=ActiveState", srvBar},
   901  			{"start", srvBar},
   902  		})
   903  }
   904  
   905  func (s *serviceControlSuite) TestRestartWithAllExplicitServices(c *C) {
   906  	srvAbc := "snap.test-snap.abc.service"
   907  	srvFoo := "snap.test-snap.foo.service"
   908  	srvBar := "snap.test-snap.bar.service"
   909  	s.testRestartWithExplicitServicesCommon(c,
   910  		[]string{srvAbc, srvBar, srvFoo},
   911  		[][]string{
   912  			{"stop", srvFoo},
   913  			{"show", "--property=ActiveState", srvFoo},
   914  			{"start", srvFoo},
   915  			{"stop", srvBar},
   916  			{"show", "--property=ActiveState", srvBar},
   917  			{"start", srvBar},
   918  			{"stop", srvAbc},
   919  			{"show", "--property=ActiveState", srvAbc},
   920  			{"start", srvAbc},
   921  		})
   922  }
   923  
   924  func (s *serviceControlSuite) TestRestartAllServices(c *C) {
   925  	st := s.state
   926  	st.Lock()
   927  	defer st.Unlock()
   928  
   929  	s.mockTestSnap(c)
   930  
   931  	chg := st.NewChange("service-control", "...")
   932  	t := st.NewTask("service-control", "...")
   933  	cmd := &servicestate.ServiceAction{
   934  		SnapName: "test-snap",
   935  		Action:   "restart",
   936  		Services: []string{"abc", "foo", "bar"},
   937  	}
   938  	t.Set("service-action", cmd)
   939  	chg.AddTask(t)
   940  
   941  	st.Unlock()
   942  	defer s.se.Stop()
   943  	err := s.o.Settle(5 * time.Second)
   944  	st.Lock()
   945  	c.Assert(err, IsNil)
   946  
   947  	c.Assert(t.Status(), Equals, state.DoneStatus)
   948  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   949  		{"stop", "snap.test-snap.foo.service"},
   950  		{"show", "--property=ActiveState", "snap.test-snap.foo.service"},
   951  		{"start", "snap.test-snap.foo.service"},
   952  		{"stop", "snap.test-snap.bar.service"},
   953  		{"show", "--property=ActiveState", "snap.test-snap.bar.service"},
   954  		{"start", "snap.test-snap.bar.service"},
   955  		{"stop", "snap.test-snap.abc.service"},
   956  		{"show", "--property=ActiveState", "snap.test-snap.abc.service"},
   957  		{"start", "snap.test-snap.abc.service"},
   958  	})
   959  }
   960  
   961  func (s *serviceControlSuite) TestReloadServices(c *C) {
   962  	st := s.state
   963  	st.Lock()
   964  	defer st.Unlock()
   965  
   966  	s.mockTestSnap(c)
   967  
   968  	chg := st.NewChange("service-control", "...")
   969  	t := st.NewTask("service-control", "...")
   970  	cmd := &servicestate.ServiceAction{
   971  		SnapName: "test-snap",
   972  		Action:   "reload-or-restart",
   973  		Services: []string{"foo"},
   974  	}
   975  	t.Set("service-action", cmd)
   976  	chg.AddTask(t)
   977  
   978  	st.Unlock()
   979  	defer s.se.Stop()
   980  	err := s.o.Settle(5 * time.Second)
   981  	st.Lock()
   982  	c.Assert(err, IsNil)
   983  
   984  	c.Assert(t.Status(), Equals, state.DoneStatus)
   985  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
   986  		{"reload-or-restart", "snap.test-snap.foo.service"},
   987  	})
   988  }
   989  
   990  func (s *serviceControlSuite) TestReloadAllServices(c *C) {
   991  	st := s.state
   992  	st.Lock()
   993  	defer st.Unlock()
   994  
   995  	s.mockTestSnap(c)
   996  
   997  	chg := st.NewChange("service-control", "...")
   998  	t := st.NewTask("service-control", "...")
   999  	cmd := &servicestate.ServiceAction{
  1000  		SnapName: "test-snap",
  1001  		Action:   "reload-or-restart",
  1002  		Services: []string{"foo", "abc", "bar"},
  1003  	}
  1004  	t.Set("service-action", cmd)
  1005  	chg.AddTask(t)
  1006  
  1007  	st.Unlock()
  1008  	defer s.se.Stop()
  1009  	err := s.o.Settle(5 * time.Second)
  1010  	st.Lock()
  1011  	c.Assert(err, IsNil)
  1012  
  1013  	c.Assert(t.Status(), Equals, state.DoneStatus)
  1014  	c.Check(s.sysctlArgs, DeepEquals, [][]string{
  1015  		{"reload-or-restart", "snap.test-snap.foo.service"},
  1016  		{"reload-or-restart", "snap.test-snap.bar.service"},
  1017  		{"reload-or-restart", "snap.test-snap.abc.service"},
  1018  	})
  1019  }
  1020  
  1021  func (s *serviceControlSuite) TestConflict(c *C) {
  1022  	st := s.state
  1023  	st.Lock()
  1024  	defer st.Unlock()
  1025  
  1026  	s.mockTestSnap(c)
  1027  
  1028  	chg := st.NewChange("service-control", "...")
  1029  	t := st.NewTask("service-control", "...")
  1030  	cmd := &servicestate.ServiceAction{
  1031  		SnapName: "test-snap",
  1032  		Action:   "reload-or-restart",
  1033  		Services: []string{"foo"},
  1034  	}
  1035  	t.Set("service-action", cmd)
  1036  	chg.AddTask(t)
  1037  
  1038  	_, err := snapstate.Remove(st, "test-snap", snap.Revision{}, nil)
  1039  	c.Assert(err, ErrorMatches, `snap "test-snap" has "service-control" change in progress`)
  1040  }
  1041  
  1042  func (s *serviceControlSuite) TestUpdateSnapstateServices(c *C) {
  1043  	var tests = []struct {
  1044  		enable                    []string
  1045  		disable                   []string
  1046  		expectedSnapstateEnabled  []string
  1047  		expectedSnapstateDisabled []string
  1048  		changed                   bool
  1049  	}{
  1050  		// These test scenarios share a single SnapState instance and accumulate
  1051  		// changes to ServicesEnabledByHooks and ServicesDisabledByHooks.
  1052  		{
  1053  			changed: false,
  1054  		},
  1055  		{
  1056  			enable: []string{"a"},
  1057  			expectedSnapstateEnabled: []string{"a"},
  1058  			changed:                  true,
  1059  		},
  1060  		// enable again does nothing
  1061  		{
  1062  			enable: []string{"a"},
  1063  			expectedSnapstateEnabled: []string{"a"},
  1064  			changed:                  false,
  1065  		},
  1066  		{
  1067  			disable:                   []string{"a"},
  1068  			expectedSnapstateDisabled: []string{"a"},
  1069  			changed:                   true,
  1070  		},
  1071  		{
  1072  			enable: []string{"a", "c"},
  1073  			expectedSnapstateEnabled: []string{"a", "c"},
  1074  			changed:                  true,
  1075  		},
  1076  		{
  1077  			disable:                   []string{"b"},
  1078  			expectedSnapstateEnabled:  []string{"a", "c"},
  1079  			expectedSnapstateDisabled: []string{"b"},
  1080  			changed:                   true,
  1081  		},
  1082  		{
  1083  			disable:                   []string{"b", "c"},
  1084  			expectedSnapstateEnabled:  []string{"a"},
  1085  			expectedSnapstateDisabled: []string{"b", "c"},
  1086  			changed:                   true,
  1087  		},
  1088  	}
  1089  
  1090  	snapst := snapstate.SnapState{}
  1091  
  1092  	for _, tst := range tests {
  1093  		var enable, disable []*snap.AppInfo
  1094  		for _, srv := range tst.enable {
  1095  			enable = append(enable, &snap.AppInfo{Name: srv})
  1096  		}
  1097  		for _, srv := range tst.disable {
  1098  			disable = append(disable, &snap.AppInfo{Name: srv})
  1099  		}
  1100  		result, err := servicestate.UpdateSnapstateServices(&snapst, enable, disable)
  1101  		c.Assert(err, IsNil)
  1102  		c.Check(result, Equals, tst.changed)
  1103  		c.Check(snapst.ServicesEnabledByHooks, DeepEquals, tst.expectedSnapstateEnabled)
  1104  		c.Check(snapst.ServicesDisabledByHooks, DeepEquals, tst.expectedSnapstateDisabled)
  1105  	}
  1106  
  1107  	services := []*snap.AppInfo{{Name: "foo"}}
  1108  	_, err := servicestate.UpdateSnapstateServices(nil, services, services)
  1109  	c.Assert(err, ErrorMatches, `internal error: cannot handle enabled and disabled services at the same time`)
  1110  }