github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/service/systemd/service_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package systemd_test
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/coreos/go-systemd/dbus"
    12  	"github.com/golang/mock/gomock"
    13  	"github.com/juju/errors"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils/exec"
    16  	"github.com/juju/utils/shell"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/names.v2"
    19  
    20  	"github.com/juju/juju/juju/paths"
    21  	"github.com/juju/juju/service"
    22  	"github.com/juju/juju/service/common"
    23  	"github.com/juju/juju/service/systemd"
    24  	systemdtesting "github.com/juju/juju/service/systemd/testing"
    25  	coretesting "github.com/juju/juju/testing"
    26  )
    27  
    28  var renderer = &shell.BashRenderer{}
    29  
    30  const confStr = `
    31  [Unit]
    32  Description=juju agent for %s
    33  After=syslog.target
    34  After=network.target
    35  After=systemd-user-sessions.service
    36  
    37  [Service]
    38  ExecStart=%s
    39  Restart=on-failure
    40  
    41  [Install]
    42  WantedBy=multi-user.target
    43  
    44  `
    45  
    46  const jujud = "/var/lib/juju/bin/jujud"
    47  
    48  var listCmdArg = exec.RunParams{
    49  	Commands: `/bin/systemctl list-unit-files --no-legend --no-page -l -t service | grep -o -P '^\w[\S]*(?=\.service)'`,
    50  }
    51  
    52  var errFailure = errors.New("you-failed")
    53  
    54  type initSystemSuite struct {
    55  	coretesting.BaseSuite
    56  
    57  	dataDir string
    58  	ch      chan string
    59  	dBus    *MockDBusAPI
    60  	fops    *systemd.MockShimFileOps
    61  	exec    *systemd.MockShimExec
    62  
    63  	name    string
    64  	tag     names.Tag
    65  	conf    common.Conf
    66  	confStr string
    67  }
    68  
    69  var _ = gc.Suite(&initSystemSuite{})
    70  
    71  func (s *initSystemSuite) SetUpTest(c *gc.C) {
    72  	s.BaseSuite.SetUpTest(c)
    73  
    74  	dataDir, err := paths.DataDir("vivid")
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	s.dataDir = dataDir
    77  
    78  	// Set up the service config.
    79  	tagStr := "machine-0"
    80  	tag, err := names.ParseTag(tagStr)
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	s.tag = tag
    83  	s.name = "jujud-" + tagStr
    84  	s.conf = common.Conf{
    85  		Desc:      "juju agent for " + tagStr,
    86  		ExecStart: jujud + " " + tagStr,
    87  	}
    88  }
    89  
    90  func (s *initSystemSuite) patch(c *gc.C) *gomock.Controller {
    91  	ctrl := gomock.NewController(c)
    92  
    93  	s.dBus = NewMockDBusAPI(ctrl)
    94  	s.ch = systemd.PatchNewChan(s)
    95  	s.fops = systemd.PatchFileOps(s, ctrl)
    96  	s.exec = systemd.PatchExec(s, ctrl)
    97  
    98  	return ctrl
    99  }
   100  
   101  func (s *initSystemSuite) newService(c *gc.C) *systemd.Service {
   102  	var fac systemd.DBusAPIFactory
   103  	if s.dBus == nil {
   104  		fac = func() (systemd.DBusAPI, error) {
   105  			return nil, errors.New("Prior call to initSystemSuite.patch required before attempting DBusAPI connection")
   106  		}
   107  	} else {
   108  		fac = func() (systemd.DBusAPI, error) { return s.dBus, nil }
   109  	}
   110  
   111  	svc, err := systemd.NewService(s.name, s.conf, "/lib/systemd/system", fac, renderer.Join(s.dataDir, "init"))
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	return svc
   114  }
   115  
   116  func (s *initSystemSuite) expectConf(c *gc.C, conf common.Conf) *gomock.Call {
   117  	data, err := systemd.Serialize(s.name, conf, renderer)
   118  	c.Assert(err, jc.ErrorIsNil)
   119  
   120  	return s.exec.EXPECT().RunCommands(
   121  		exec.RunParams{
   122  			Commands: "cat /lib/systemd/system/jujud-machine-0/jujud-machine-0.service",
   123  		},
   124  	).Return(&exec.ExecResponse{Stdout: data}, nil)
   125  }
   126  
   127  func (s *initSystemSuite) newConfStr(name string) string {
   128  	return s.newConfStrCmd(name, "")
   129  }
   130  
   131  func (s *initSystemSuite) newConfStrCmd(name, cmd string) string {
   132  	tag := name[len("jujud-"):]
   133  	if cmd == "" {
   134  		cmd = jujud + " " + tag
   135  	}
   136  	return fmt.Sprintf(confStr[1:], tag, cmd)
   137  }
   138  
   139  func (s *initSystemSuite) newConfStrEnv(name, env string) string {
   140  	const replace = "[Service]\n"
   141  	result := s.newConfStr(name)
   142  	result = strings.Replace(
   143  		result, replace,
   144  		fmt.Sprintf("%sEnvironment=%s\n", replace, env),
   145  		1,
   146  	)
   147  	return result
   148  }
   149  
   150  func (s *initSystemSuite) TestListServices(c *gc.C) {
   151  	ctrl := s.patch(c)
   152  	defer ctrl.Finish()
   153  
   154  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{
   155  		Stdout: []byte("jujud-machine-0\njujud-unit-wordpress-0"),
   156  	}, nil)
   157  
   158  	services, err := systemd.ListServices()
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	c.Check(services, jc.SameContents, []string{"jujud-machine-0", "jujud-unit-wordpress-0"})
   161  }
   162  
   163  func (s *initSystemSuite) TestListServicesEmpty(c *gc.C) {
   164  	ctrl := s.patch(c)
   165  	defer ctrl.Finish()
   166  
   167  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil)
   168  
   169  	services, err := systemd.ListServices()
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	c.Check(services, gc.HasLen, 0)
   172  }
   173  
   174  func (s *initSystemSuite) TestNewService(c *gc.C) {
   175  	svc := s.newService(c)
   176  	c.Check(svc.Service, jc.DeepEquals, common.Service{Name: s.name, Conf: s.conf})
   177  	c.Check(svc.ConfName, gc.Equals, s.name+".service")
   178  	c.Check(svc.UnitName, gc.Equals, s.name+".service")
   179  	c.Check(svc.DirName, gc.Equals, fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name))
   180  }
   181  
   182  func (s *initSystemSuite) TestNewServiceLogfile(c *gc.C) {
   183  	s.conf.Logfile = "/var/log/juju/machine-0.log"
   184  	svc := s.newService(c)
   185  
   186  	user, group := systemd.SyslogUserGroup()
   187  	dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)
   188  	script := `
   189  #!/usr/bin/env bash
   190  
   191  # Set up logging.
   192  touch '/var/log/juju/machine-0.log'
   193  chown `[1:] + user + `:` + group + ` '/var/log/juju/machine-0.log'
   194  chmod 0600 '/var/log/juju/machine-0.log'
   195  exec >> '/var/log/juju/machine-0.log'
   196  exec 2>&1
   197  
   198  # Run the script.
   199  ` + jujud + " machine-0"
   200  
   201  	c.Check(svc.Service, jc.DeepEquals, common.Service{
   202  		Name: s.name,
   203  		Conf: common.Conf{
   204  			Desc:      s.conf.Desc,
   205  			ExecStart: dirName + "/exec-start.sh",
   206  			Logfile:   "/var/log/juju/machine-0.log",
   207  		},
   208  	})
   209  
   210  	c.Check(svc.ConfName, gc.Equals, s.name+".service")
   211  	c.Check(svc.UnitName, gc.Equals, s.name+".service")
   212  	c.Check(svc.DirName, gc.Equals, dirName)
   213  
   214  	// This gives us a more readable output if they aren't equal.
   215  	c.Check(string(svc.Script), gc.Equals, script)
   216  	c.Check(strings.Split(string(svc.Script), "\n"), jc.DeepEquals, strings.Split(script, "\n"))
   217  }
   218  
   219  func (s *initSystemSuite) TestNewServiceEmptyConf(c *gc.C) {
   220  	svc, err := systemd.NewService(s.name, common.Conf{}, "/lib/systemd/system", systemd.NewDBusAPI, renderer.Join(s.dataDir, "init"))
   221  	c.Assert(err, gc.IsNil)
   222  	c.Check(svc.Service, jc.DeepEquals, common.Service{Name: s.name})
   223  	c.Check(svc.ConfName, gc.Equals, s.name+".service")
   224  	c.Check(svc.UnitName, gc.Equals, s.name+".service")
   225  	c.Check(svc.DirName, gc.Equals, fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name))
   226  }
   227  
   228  func (s *initSystemSuite) TestNewServiceBasic(c *gc.C) {
   229  	s.conf.ExecStart = "/path/to/some/other/command"
   230  	svc := s.newService(c)
   231  	c.Check(svc.Service, jc.DeepEquals, common.Service{Name: s.name, Conf: s.conf})
   232  	c.Check(svc.ConfName, gc.Equals, s.name+".service")
   233  	c.Check(svc.UnitName, gc.Equals, s.name+".service")
   234  	c.Check(svc.DirName, gc.Equals, fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name))
   235  }
   236  
   237  func (s *initSystemSuite) TestNewServiceExtraScript(c *gc.C) {
   238  	s.conf.ExtraScript = "'/path/to/another/command'"
   239  	svc := s.newService(c)
   240  
   241  	dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)
   242  	script := `
   243  #!/usr/bin/env bash
   244  
   245  '/path/to/another/command'
   246  `[1:] + jujud + " machine-0"
   247  
   248  	c.Check(svc.Service, jc.DeepEquals, common.Service{
   249  		Name: s.name,
   250  		Conf: common.Conf{
   251  			Desc:      s.conf.Desc,
   252  			ExecStart: dirName + "/exec-start.sh",
   253  		},
   254  	})
   255  
   256  	c.Check(svc.ConfName, gc.Equals, s.name+".service")
   257  	c.Check(svc.UnitName, gc.Equals, s.name+".service")
   258  	c.Check(svc.DirName, gc.Equals, dirName)
   259  	c.Check(string(svc.Script), gc.Equals, script)
   260  }
   261  
   262  func (s *initSystemSuite) TestNewServiceMultiLine(c *gc.C) {
   263  	s.conf.ExecStart = "a\nb\nc"
   264  	svc := s.newService(c)
   265  
   266  	dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)
   267  	script := `
   268  #!/usr/bin/env bash
   269  
   270  a
   271  b
   272  c`[1:]
   273  
   274  	c.Check(svc.Service, jc.DeepEquals, common.Service{
   275  		Name: s.name,
   276  		Conf: common.Conf{
   277  			Desc:      s.conf.Desc,
   278  			ExecStart: dirName + "/exec-start.sh",
   279  		},
   280  	})
   281  
   282  	c.Check(svc.ConfName, gc.Equals, s.name+".service")
   283  	c.Check(svc.UnitName, gc.Equals, s.name+".service")
   284  	c.Check(svc.DirName, gc.Equals, dirName)
   285  
   286  	// This gives us a more readable output if they aren't equal.
   287  	c.Check(string(svc.Script), gc.Equals, script)
   288  }
   289  
   290  func (s *initSystemSuite) TestInstalledTrue(c *gc.C) {
   291  	ctrl := s.patch(c)
   292  	defer ctrl.Finish()
   293  
   294  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{
   295  		Stdout: []byte("jujud-machine-0\njuju-mongod"),
   296  	}, nil)
   297  
   298  	installed, err := s.newService(c).Installed()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	c.Check(installed, jc.IsTrue)
   301  }
   302  
   303  func (s *initSystemSuite) TestInstalledFalse(c *gc.C) {
   304  	ctrl := s.patch(c)
   305  	defer ctrl.Finish()
   306  
   307  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{
   308  		Stdout: []byte("some-other-service"),
   309  	}, nil)
   310  
   311  	installed, err := s.newService(c).Installed()
   312  	c.Assert(err, jc.ErrorIsNil)
   313  	c.Check(installed, jc.IsFalse)
   314  }
   315  
   316  func (s *initSystemSuite) TestInstalledError(c *gc.C) {
   317  	ctrl := s.patch(c)
   318  	defer ctrl.Finish()
   319  
   320  	s.exec.EXPECT().RunCommands(listCmdArg).Return(nil, errFailure)
   321  
   322  	installed, err := s.newService(c).Installed()
   323  	c.Assert(errors.Cause(err), gc.Equals, errFailure)
   324  	c.Check(installed, jc.IsFalse)
   325  }
   326  
   327  func (s *initSystemSuite) TestExistsTrue(c *gc.C) {
   328  	ctrl := s.patch(c)
   329  	defer ctrl.Finish()
   330  	s.expectConf(c, s.conf)
   331  
   332  	exists, err := s.newService(c).Exists()
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	c.Check(exists, jc.IsTrue)
   335  }
   336  
   337  func (s *initSystemSuite) TestExistsFalse(c *gc.C) {
   338  	ctrl := s.patch(c)
   339  	defer ctrl.Finish()
   340  
   341  	// We force the systemd API to return a slightly different conf.
   342  	// In this case we simply set Conf.Env, which s.conf does not set.
   343  	// This causes Service.Exists to return false.
   344  	s.expectConf(c, common.Conf{
   345  		Desc:      s.conf.Desc,
   346  		ExecStart: s.conf.ExecStart,
   347  		Env:       map[string]string{"a": "b"},
   348  	})
   349  
   350  	exists, err := s.newService(c).Exists()
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	c.Check(exists, jc.IsFalse)
   353  }
   354  
   355  func (s *initSystemSuite) TestExistsError(c *gc.C) {
   356  	ctrl := s.patch(c)
   357  	defer ctrl.Finish()
   358  
   359  	s.exec.EXPECT().RunCommands(
   360  		exec.RunParams{
   361  			Commands: "cat /lib/systemd/system/jujud-machine-0/jujud-machine-0.service",
   362  		},
   363  	).Return(nil, errFailure)
   364  
   365  	exists, err := s.newService(c).Exists()
   366  	c.Assert(errors.Cause(err), gc.Equals, errFailure)
   367  	c.Check(exists, jc.IsFalse)
   368  }
   369  
   370  func (s *initSystemSuite) TestExistsEmptyConf(c *gc.C) {
   371  	svc := s.newService(c)
   372  	svc.Service.Conf = common.Conf{}
   373  	_, err := svc.Exists()
   374  	c.Check(err, gc.ErrorMatches, `.*no conf expected.*`)
   375  }
   376  
   377  func (s *initSystemSuite) TestRunningTrue(c *gc.C) {
   378  	ctrl := s.patch(c)
   379  	defer ctrl.Finish()
   380  
   381  	gomock.InOrder(
   382  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   383  			{Name: "jujud-machine-0.service", LoadState: "loaded", ActiveState: "active"},
   384  			{Name: "juju-mongod.service", LoadState: "loaded", ActiveState: "active"},
   385  		}, nil),
   386  		s.dBus.EXPECT().Close(),
   387  	)
   388  
   389  	running, err := s.newService(c).Running()
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	c.Check(running, jc.IsTrue)
   392  }
   393  
   394  func (s *initSystemSuite) TestRunningFalse(c *gc.C) {
   395  	ctrl := s.patch(c)
   396  	defer ctrl.Finish()
   397  
   398  	gomock.InOrder(
   399  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   400  			{Name: "jujud-machine-0.service", LoadState: "loaded", ActiveState: "inactive"},
   401  			{Name: "juju-mongod.service", LoadState: "loaded", ActiveState: "active"},
   402  		}, nil),
   403  		s.dBus.EXPECT().Close(),
   404  	)
   405  
   406  	running, err := s.newService(c).Running()
   407  	c.Assert(err, jc.ErrorIsNil)
   408  	c.Check(running, jc.IsFalse)
   409  }
   410  
   411  func (s *initSystemSuite) TestRunningNotEnabled(c *gc.C) {
   412  	ctrl := s.patch(c)
   413  	defer ctrl.Finish()
   414  
   415  	gomock.InOrder(
   416  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   417  			{Name: "random-thing.service", LoadState: "loaded", ActiveState: "active"},
   418  		}, nil),
   419  		s.dBus.EXPECT().Close(),
   420  	)
   421  
   422  	running, err := s.newService(c).Running()
   423  	c.Assert(err, jc.ErrorIsNil)
   424  	c.Check(running, jc.IsFalse)
   425  }
   426  
   427  func (s *initSystemSuite) TestRunningError(c *gc.C) {
   428  	ctrl := s.patch(c)
   429  	defer ctrl.Finish()
   430  
   431  	gomock.InOrder(
   432  		s.dBus.EXPECT().ListUnits().Return(nil, errFailure),
   433  		s.dBus.EXPECT().Close(),
   434  	)
   435  
   436  	_, err := s.newService(c).Running()
   437  	c.Check(errors.Cause(err), gc.Equals, errFailure)
   438  }
   439  
   440  func (s *initSystemSuite) TestStart(c *gc.C) {
   441  	ctrl := s.patch(c)
   442  	defer ctrl.Finish()
   443  
   444  	svc := s.newService(c)
   445  
   446  	gomock.InOrder(
   447  		s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{
   448  			Stdout: []byte("jujud-machine-0\njuju-mongod"),
   449  		}, nil),
   450  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   451  			{Name: svc.UnitName, LoadState: "loaded", ActiveState: "inactive"},
   452  		}, nil),
   453  		s.dBus.EXPECT().Close(),
   454  
   455  		// Equality check for the channel fails here, so we use Any().
   456  		// We know this is safe, because we notify on the channel we got from
   457  		// the patched call and everything proceeds happily.
   458  		s.dBus.EXPECT().StartUnit(svc.UnitName, "fail", gomock.Any()).Return(1, nil).Do(
   459  			func(_ ...interface{}) { s.ch <- "done" },
   460  		),
   461  		s.dBus.EXPECT().Close(),
   462  	)
   463  
   464  	err := svc.Start()
   465  	c.Assert(err, jc.ErrorIsNil)
   466  }
   467  
   468  func (s *initSystemSuite) TestStartAlreadyRunning(c *gc.C) {
   469  	ctrl := s.patch(c)
   470  	defer ctrl.Finish()
   471  
   472  	svc := s.newService(c)
   473  
   474  	gomock.InOrder(
   475  		s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{
   476  			Stdout: []byte("jujud-machine-0\njuju-mongod"),
   477  		}, nil),
   478  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   479  			{Name: svc.UnitName, LoadState: "loaded", ActiveState: "active"},
   480  		}, nil),
   481  		s.dBus.EXPECT().Close(),
   482  	)
   483  
   484  	err := svc.Start()
   485  	c.Assert(err, jc.ErrorIsNil)
   486  }
   487  
   488  func (s *initSystemSuite) TestStartNotInstalled(c *gc.C) {
   489  	ctrl := s.patch(c)
   490  	defer ctrl.Finish()
   491  
   492  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil)
   493  
   494  	err := s.newService(c).Start()
   495  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   496  }
   497  
   498  func (s *initSystemSuite) TestStop(c *gc.C) {
   499  	ctrl := s.patch(c)
   500  	defer ctrl.Finish()
   501  
   502  	svc := s.newService(c)
   503  
   504  	gomock.InOrder(
   505  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   506  			{Name: svc.UnitName, LoadState: "loaded", ActiveState: "active"},
   507  		}, nil),
   508  		s.dBus.EXPECT().Close(),
   509  
   510  		// Equality check for the channel fails here, so we use Any().
   511  		// We know this is safe, because we notify on the channel we got from
   512  		// the patched call and everything proceeds happily.
   513  		s.dBus.EXPECT().StopUnit(svc.UnitName, "fail", gomock.Any()).Return(1, nil).Do(
   514  			func(_ ...interface{}) { s.ch <- "done" },
   515  		),
   516  		s.dBus.EXPECT().Close(),
   517  	)
   518  
   519  	err := svc.Stop()
   520  	c.Assert(err, jc.ErrorIsNil)
   521  }
   522  
   523  func (s *initSystemSuite) TestStopNotRunning(c *gc.C) {
   524  	ctrl := s.patch(c)
   525  	defer ctrl.Finish()
   526  
   527  	svc := s.newService(c)
   528  
   529  	gomock.InOrder(
   530  		s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   531  			{Name: svc.UnitName, LoadState: "loaded", ActiveState: "inactive"},
   532  		}, nil),
   533  		s.dBus.EXPECT().Close(),
   534  	)
   535  
   536  	err := svc.Stop()
   537  	c.Assert(err, jc.ErrorIsNil)
   538  }
   539  
   540  func (s *initSystemSuite) TestStopNotInstalled(c *gc.C) {
   541  	ctrl := s.patch(c)
   542  	defer ctrl.Finish()
   543  
   544  	gomock.InOrder(
   545  		s.dBus.EXPECT().ListUnits().Return(nil, nil),
   546  		s.dBus.EXPECT().Close(),
   547  	)
   548  
   549  	err := s.newService(c).Stop()
   550  	c.Assert(err, jc.ErrorIsNil)
   551  }
   552  
   553  func (s *initSystemSuite) TestRemove(c *gc.C) {
   554  	ctrl := s.patch(c)
   555  	defer ctrl.Finish()
   556  
   557  	svc := s.newService(c)
   558  
   559  	gomock.InOrder(
   560  		s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte(svc.Name())}, nil),
   561  		s.dBus.EXPECT().DisableUnitFiles([]string{svc.UnitName}, false).Return(nil, nil),
   562  		s.dBus.EXPECT().Reload().Return(nil),
   563  		s.fops.EXPECT().RemoveAll(svc.DirName).Return(nil),
   564  		s.dBus.EXPECT().Close(),
   565  	)
   566  
   567  	err := svc.Remove()
   568  	c.Assert(err, jc.ErrorIsNil)
   569  }
   570  
   571  func (s *initSystemSuite) TestRemoveNotInstalled(c *gc.C) {
   572  	ctrl := s.patch(c)
   573  	defer ctrl.Finish()
   574  
   575  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil)
   576  
   577  	err := s.newService(c).Remove()
   578  	c.Assert(err, jc.ErrorIsNil)
   579  }
   580  
   581  func (s *initSystemSuite) TestInstall(c *gc.C) {
   582  	ctrl := s.patch(c)
   583  	defer ctrl.Finish()
   584  
   585  	dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)
   586  	fileName := fmt.Sprintf("%s/%s.service", dirName, s.name)
   587  
   588  	gomock.InOrder(
   589  		s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil),
   590  		s.fops.EXPECT().MkdirAll(dirName).Return(nil),
   591  		s.fops.EXPECT().CreateFile(fileName, []byte(s.newConfStr(s.name)), os.FileMode(0644)).Return(nil),
   592  		s.dBus.EXPECT().LinkUnitFiles([]string{fileName}, false, true).Return(nil, nil),
   593  		s.dBus.EXPECT().Reload().Return(nil),
   594  		s.dBus.EXPECT().EnableUnitFiles([]string{fileName}, false, true).Return(true, nil, nil),
   595  		s.dBus.EXPECT().Close(),
   596  	)
   597  
   598  	err := s.newService(c).Install()
   599  	c.Assert(err, jc.ErrorIsNil)
   600  }
   601  
   602  func (s *initSystemSuite) TestInstallAlreadyInstalled(c *gc.C) {
   603  	ctrl := s.patch(c)
   604  	defer ctrl.Finish()
   605  
   606  	s.expectConf(c, s.conf)
   607  	svc := s.newService(c)
   608  
   609  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte(svc.Name())}, nil)
   610  
   611  	err := svc.Install()
   612  	c.Assert(err, jc.ErrorIsNil)
   613  }
   614  
   615  func (s *initSystemSuite) TestInstallZombie(c *gc.C) {
   616  	ctrl := s.patch(c)
   617  	defer ctrl.Finish()
   618  
   619  	// We force the systemd API to return a slightly different conf.
   620  	// In this case we simply set a different Env value between the
   621  	// conf we are installing and the conf returned by the systemd API.
   622  	// This causes Service.Exists to return false.
   623  	conf := common.Conf{
   624  		Desc:      s.conf.Desc,
   625  		ExecStart: s.conf.ExecStart,
   626  		Env:       map[string]string{"a": "b"},
   627  	}
   628  	s.expectConf(c, conf)
   629  	conf.Env["a"] = "c"
   630  	svc, err := systemd.NewService(s.name, conf, "/lib/systemd/system", func() (systemd.DBusAPI, error) { return s.dBus, nil }, renderer.Join(s.dataDir, "init"))
   631  
   632  	dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)
   633  	fileName := fmt.Sprintf("%s/%s.service", dirName, s.name)
   634  
   635  	s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte(svc.Name())}, nil).Times(2)
   636  	s.dBus.EXPECT().Close().Times(3)
   637  
   638  	s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{
   639  		{Name: svc.Name(), LoadState: "loaded", ActiveState: "active"},
   640  	}, nil)
   641  	s.dBus.EXPECT().DisableUnitFiles([]string{svc.UnitName}, false).Return(nil, nil)
   642  	s.dBus.EXPECT().Reload().Return(nil)
   643  	s.fops.EXPECT().RemoveAll(svc.DirName).Return(nil)
   644  	s.fops.EXPECT().MkdirAll(dirName).Return(nil)
   645  	s.fops.EXPECT().CreateFile(fileName, []byte(s.newConfStrEnv(s.name, `"a=c"`)), os.FileMode(0644)).Return(nil)
   646  	s.dBus.EXPECT().LinkUnitFiles([]string{fileName}, false, true).Return(nil, nil)
   647  	s.dBus.EXPECT().Reload().Return(nil)
   648  	s.dBus.EXPECT().EnableUnitFiles([]string{fileName}, false, true).Return(true, nil, nil)
   649  
   650  	err = svc.Install()
   651  	c.Assert(err, jc.ErrorIsNil)
   652  }
   653  
   654  func (s *initSystemSuite) TestInstallMultiLine(c *gc.C) {
   655  	ctrl := s.patch(c)
   656  	defer ctrl.Finish()
   657  
   658  	dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)
   659  	fileName := fmt.Sprintf("%s/%s.service", dirName, s.name)
   660  	scriptPath := fmt.Sprintf("%s/exec-start.sh", dirName)
   661  	cmd := "a\nb\nc"
   662  
   663  	svc := s.newService(c)
   664  	svc.Service.Conf.ExecStart = scriptPath
   665  	svc.Script = []byte(cmd)
   666  
   667  	gomock.InOrder(
   668  		s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil),
   669  		s.fops.EXPECT().MkdirAll(dirName).Return(nil),
   670  		s.fops.EXPECT().CreateFile(scriptPath, []byte(cmd), os.FileMode(0755)).Return(nil),
   671  		s.fops.EXPECT().CreateFile(fileName, []byte(s.newConfStrCmd(s.name, scriptPath)), os.FileMode(0644)).Return(nil),
   672  		s.dBus.EXPECT().LinkUnitFiles([]string{fileName}, false, true).Return(nil, nil),
   673  		s.dBus.EXPECT().Reload().Return(nil),
   674  		s.dBus.EXPECT().EnableUnitFiles([]string{fileName}, false, true).Return(true, nil, nil),
   675  		s.dBus.EXPECT().Close(),
   676  	)
   677  
   678  	err := svc.Install()
   679  	c.Assert(err, jc.ErrorIsNil)
   680  }
   681  
   682  func (s *initSystemSuite) TestInstallEmptyConf(c *gc.C) {
   683  	svc := s.newService(c)
   684  	svc.Service.Conf = common.Conf{}
   685  	err := svc.Install()
   686  	c.Check(err, gc.ErrorMatches, `.*missing conf.*`)
   687  }
   688  
   689  func (s *initSystemSuite) TestInstallCommands(c *gc.C) {
   690  	name := "jujud-machine-0"
   691  	commands, err := s.newService(c).InstallCommands()
   692  	c.Assert(err, jc.ErrorIsNil)
   693  
   694  	test := systemdtesting.WriteConfTest{
   695  		Service:  name,
   696  		DataDir:  "/lib/systemd/system",
   697  		Expected: s.newConfStr(name),
   698  	}
   699  	test.CheckCommands(c, commands)
   700  }
   701  
   702  func (s *initSystemSuite) TestInstallCommandsLogfile(c *gc.C) {
   703  	name := "jujud-machine-0"
   704  	s.conf.Logfile = "/var/log/juju/machine-0.log"
   705  	svc := s.newService(c)
   706  	commands, err := svc.InstallCommands()
   707  	c.Assert(err, jc.ErrorIsNil)
   708  
   709  	user, group := systemd.SyslogUserGroup()
   710  	test := systemdtesting.WriteConfTest{
   711  		Service: name,
   712  		DataDir: "/lib/systemd/system",
   713  		Expected: strings.Replace(
   714  			s.newConfStr(name),
   715  			"ExecStart=/var/lib/juju/bin/jujud machine-0",
   716  			"ExecStart=/lib/systemd/system/jujud-machine-0/exec-start.sh",
   717  			-1),
   718  		Script: `
   719  # Set up logging.
   720  touch '/var/log/juju/machine-0.log'
   721  chown `[1:] + user + `:` + group + ` '/var/log/juju/machine-0.log'
   722  chmod 0600 '/var/log/juju/machine-0.log'
   723  exec >> '/var/log/juju/machine-0.log'
   724  exec 2>&1
   725  
   726  # Run the script.
   727  ` + jujud + " machine-0",
   728  	}
   729  
   730  	test.CheckCommands(c, commands)
   731  }
   732  
   733  func (s *initSystemSuite) TestInstallCommandsShutdown(c *gc.C) {
   734  	name := "juju-shutdown-job"
   735  	conf, err := service.ShutdownAfterConf("cloud-final")
   736  	c.Assert(err, jc.ErrorIsNil)
   737  
   738  	svc, err := systemd.NewService(name, conf, "/lib/systemd/system", systemd.NewDBusAPI, renderer.Join(s.dataDir, "init"))
   739  	c.Assert(err, jc.ErrorIsNil)
   740  
   741  	commands, err := svc.InstallCommands()
   742  	c.Assert(err, jc.ErrorIsNil)
   743  
   744  	test := systemdtesting.WriteConfTest{
   745  		Service: name,
   746  		DataDir: "/lib/systemd/system",
   747  		Expected: `
   748  [Unit]
   749  Description=juju shutdown job
   750  After=syslog.target
   751  After=network.target
   752  After=systemd-user-sessions.service
   753  After=cloud-final
   754  
   755  [Service]
   756  ExecStart=/sbin/shutdown -h now
   757  ExecStopPost=/bin/systemctl disable juju-shutdown-job.service
   758  
   759  [Install]
   760  WantedBy=multi-user.target
   761  `[1:],
   762  	}
   763  
   764  	test.CheckCommands(c, commands)
   765  }
   766  
   767  func (s *initSystemSuite) TestInstallCommandsEmptyConf(c *gc.C) {
   768  	svc := s.newService(c)
   769  	svc.Service.Conf = common.Conf{}
   770  	_, err := svc.InstallCommands()
   771  	c.Check(err, gc.ErrorMatches, `.*missing conf.*`)
   772  }
   773  
   774  func (s *initSystemSuite) TestStartCommands(c *gc.C) {
   775  	commands, err := s.newService(c).StartCommands()
   776  	c.Assert(err, jc.ErrorIsNil)
   777  	c.Check(commands, jc.DeepEquals, []string{"/bin/systemctl start jujud-machine-0.service"})
   778  }