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