github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	user, group := systemd.SyslogUserGroup()
   241  	dirname := fmt.Sprintf("%s/init/%s", s.dataDir, s.name)
   242  	script := `
   243  #!/usr/bin/env bash
   244  
   245  # Set up logging.
   246  touch '/var/log/juju/machine-0.log'
   247  chown `[1:] + user + `:` + group + ` '/var/log/juju/machine-0.log'
   248  chmod 0600 '/var/log/juju/machine-0.log'
   249  exec >> '/var/log/juju/machine-0.log'
   250  exec 2>&1
   251  
   252  # Run the script.
   253  ` + jujud + " machine-0"
   254  	c.Check(service, jc.DeepEquals, &systemd.Service{
   255  		Service: common.Service{
   256  			Name: s.name,
   257  			Conf: common.Conf{
   258  				Desc:      s.conf.Desc,
   259  				ExecStart: dirname + "/exec-start.sh",
   260  				Logfile:   "/var/log/juju/machine-0.log",
   261  			},
   262  		},
   263  		UnitName: s.name + ".service",
   264  		ConfName: s.name + ".service",
   265  		Dirname:  dirname,
   266  		Script:   []byte(script),
   267  	})
   268  	// This gives us a more readable output if they aren't equal.
   269  	c.Check(string(service.Script), gc.Equals, script)
   270  	c.Check(strings.Split(string(service.Script), "\n"), jc.DeepEquals, strings.Split(script, "\n"))
   271  }
   272  
   273  func (s *initSystemSuite) TestNewServiceEmptyConf(c *gc.C) {
   274  	service, err := systemd.NewService(s.name, common.Conf{}, s.dataDir)
   275  	c.Assert(err, jc.ErrorIsNil)
   276  
   277  	c.Check(service, jc.DeepEquals, &systemd.Service{
   278  		Service: common.Service{
   279  			Name: s.name,
   280  		},
   281  		ConfName: s.name + ".service",
   282  		UnitName: s.name + ".service",
   283  		Dirname:  fmt.Sprintf("%s/init/%s", s.dataDir, s.name),
   284  	})
   285  	s.stub.CheckCalls(c, nil)
   286  }
   287  
   288  func (s *initSystemSuite) TestNewServiceBasic(c *gc.C) {
   289  	s.conf.ExecStart = "/path/to/some/other/command"
   290  	svc := s.newService(c)
   291  
   292  	c.Check(svc, jc.DeepEquals, &systemd.Service{
   293  		Service: common.Service{
   294  			Name: s.name,
   295  			Conf: s.conf,
   296  		},
   297  		ConfName: s.name + ".service",
   298  		UnitName: s.name + ".service",
   299  		Dirname:  fmt.Sprintf("%s/init/%s", s.dataDir, s.name),
   300  	})
   301  	s.stub.CheckCalls(c, nil)
   302  }
   303  
   304  func (s *initSystemSuite) TestNewServiceExtraScript(c *gc.C) {
   305  	s.conf.ExtraScript = "'/path/to/another/command'"
   306  	svc := s.newService(c)
   307  
   308  	dirname := fmt.Sprintf("%s/init/%s", s.dataDir, s.name)
   309  	script := `
   310  #!/usr/bin/env bash
   311  
   312  '/path/to/another/command'
   313  `[1:] + jujud + " machine-0"
   314  	c.Check(svc, jc.DeepEquals, &systemd.Service{
   315  		Service: common.Service{
   316  			Name: s.name,
   317  			Conf: common.Conf{
   318  				Desc:      s.conf.Desc,
   319  				ExecStart: dirname + "/exec-start.sh",
   320  			},
   321  		},
   322  		UnitName: s.name + ".service",
   323  		ConfName: s.name + ".service",
   324  		Dirname:  dirname,
   325  		Script:   []byte(script),
   326  	})
   327  	// This gives us a more readable output if they aren't equal.
   328  	c.Check(string(svc.Script), gc.Equals, script)
   329  	s.stub.CheckCalls(c, nil)
   330  }
   331  
   332  func (s *initSystemSuite) TestNewServiceMultiline(c *gc.C) {
   333  	s.conf.ExecStart = "a\nb\nc"
   334  	svc := s.newService(c)
   335  
   336  	dirname := fmt.Sprintf("%s/init/%s", s.dataDir, s.name)
   337  	script := `
   338  #!/usr/bin/env bash
   339  
   340  a
   341  b
   342  c`[1:]
   343  	c.Check(svc, jc.DeepEquals, &systemd.Service{
   344  		Service: common.Service{
   345  			Name: s.name,
   346  			Conf: common.Conf{
   347  				Desc:      s.conf.Desc,
   348  				ExecStart: dirname + "/exec-start.sh",
   349  			},
   350  		},
   351  		UnitName: s.name + ".service",
   352  		ConfName: s.name + ".service",
   353  		Dirname:  dirname,
   354  		Script:   []byte(script),
   355  	})
   356  	// This gives us a more readable output if they aren't equal.
   357  	c.Check(string(svc.Script), gc.Equals, script)
   358  	s.stub.CheckCalls(c, nil)
   359  }
   360  
   361  func (s *initSystemSuite) TestInstalledTrue(c *gc.C) {
   362  	s.addService("jujud-machine-0", "active")
   363  	s.addService("something-else", "error")
   364  	s.addService("juju-mongod", "active")
   365  	s.addListResponse()
   366  
   367  	installed, err := s.service.Installed()
   368  	c.Assert(err, jc.ErrorIsNil)
   369  
   370  	c.Check(installed, jc.IsTrue)
   371  	s.stub.CheckCallNames(c, "RunCommand")
   372  }
   373  
   374  func (s *initSystemSuite) TestInstalledFalse(c *gc.C) {
   375  	s.addService("something-else", "error")
   376  	s.addListResponse()
   377  
   378  	installed, err := s.service.Installed()
   379  	c.Assert(err, jc.ErrorIsNil)
   380  
   381  	c.Check(installed, jc.IsFalse)
   382  	s.stub.CheckCallNames(c, "RunCommand")
   383  }
   384  
   385  func (s *initSystemSuite) TestInstalledError(c *gc.C) {
   386  	s.addService("jujud-machine-0", "active")
   387  	s.addService("something-else", "error")
   388  	s.addService("juju-mongod", "active")
   389  	s.addListResponse()
   390  	failure := errors.New("<failed>")
   391  	s.stub.SetErrors(failure)
   392  
   393  	installed, err := s.service.Installed()
   394  	c.Assert(errors.Cause(err), gc.Equals, failure)
   395  
   396  	c.Check(installed, jc.IsFalse)
   397  	s.stub.CheckCallNames(c, "RunCommand")
   398  }
   399  
   400  func (s *initSystemSuite) TestExistsTrue(c *gc.C) {
   401  	s.setConf(c, s.conf)
   402  
   403  	exists, err := s.service.Exists()
   404  	c.Assert(err, jc.ErrorIsNil)
   405  
   406  	c.Check(exists, jc.IsTrue)
   407  	s.stub.CheckCallNames(c, "RunCommand")
   408  }
   409  
   410  func (s *initSystemSuite) TestExistsFalse(c *gc.C) {
   411  	// We force the systemd API to return a slightly different conf.
   412  	// In this case we simply set Conf.Env, which s.conf does not set.
   413  	// This causes Service.Exists to return false.
   414  	s.setConf(c, common.Conf{
   415  		Desc:      s.conf.Desc,
   416  		ExecStart: s.conf.ExecStart,
   417  		Env:       map[string]string{"a": "b"},
   418  	})
   419  
   420  	exists, err := s.service.Exists()
   421  	c.Assert(err, jc.ErrorIsNil)
   422  
   423  	c.Check(exists, jc.IsFalse)
   424  	s.stub.CheckCallNames(c, "RunCommand")
   425  }
   426  
   427  func (s *initSystemSuite) TestExistsError(c *gc.C) {
   428  	failure := errors.New("<failed>")
   429  	s.stub.SetErrors(failure)
   430  
   431  	exists, err := s.service.Exists()
   432  	c.Assert(errors.Cause(err), gc.Equals, failure)
   433  
   434  	c.Check(exists, jc.IsFalse)
   435  	s.stub.CheckCallNames(c, "RunCommand")
   436  }
   437  
   438  func (s *initSystemSuite) TestExistsEmptyConf(c *gc.C) {
   439  	s.service.Service.Conf = common.Conf{}
   440  
   441  	_, err := s.service.Exists()
   442  
   443  	c.Check(err, gc.ErrorMatches, `.*no conf expected.*`)
   444  	s.stub.CheckCalls(c, nil)
   445  }
   446  
   447  func (s *initSystemSuite) TestRunningTrue(c *gc.C) {
   448  	s.addService("jujud-machine-0", "active")
   449  	s.addService("something-else", "error")
   450  	s.addService("juju-mongod", "active")
   451  
   452  	running, err := s.service.Running()
   453  	c.Assert(err, jc.ErrorIsNil)
   454  
   455  	c.Check(running, jc.IsTrue)
   456  	s.stub.CheckCallNames(c, "ListUnits", "Close")
   457  }
   458  
   459  func (s *initSystemSuite) TestRunningFalse(c *gc.C) {
   460  	s.addService("jujud-machine-0", "inactive")
   461  	s.addService("something-else", "error")
   462  	s.addService("juju-mongod", "active")
   463  
   464  	running, err := s.service.Running()
   465  	c.Assert(err, jc.ErrorIsNil)
   466  
   467  	c.Check(running, jc.IsFalse)
   468  	s.stub.CheckCallNames(c, "ListUnits", "Close")
   469  }
   470  
   471  func (s *initSystemSuite) TestRunningNotEnabled(c *gc.C) {
   472  	s.addService("something-else", "active")
   473  
   474  	running, err := s.service.Running()
   475  	c.Assert(err, jc.ErrorIsNil)
   476  
   477  	c.Check(running, jc.IsFalse)
   478  	s.stub.CheckCallNames(c, "ListUnits", "Close")
   479  }
   480  
   481  func (s *initSystemSuite) TestRunningError(c *gc.C) {
   482  	s.addService("jujud-machine-0", "active")
   483  	s.addService("something-else", "error")
   484  	s.addService("juju-mongod", "active")
   485  	failure := errors.New("<failed>")
   486  	s.stub.SetErrors(failure)
   487  
   488  	running, err := s.service.Running()
   489  	c.Assert(errors.Cause(err), gc.Equals, failure)
   490  
   491  	c.Check(running, jc.IsFalse)
   492  	s.stub.CheckCallNames(c, "ListUnits", "Close")
   493  }
   494  
   495  func (s *initSystemSuite) TestStart(c *gc.C) {
   496  	s.addService("jujud-machine-0", "inactive")
   497  	s.ch <- "done"
   498  	s.addListResponse()
   499  
   500  	err := s.service.Start()
   501  	c.Assert(err, jc.ErrorIsNil)
   502  
   503  	s.stub.CheckCalls(c, []testing.StubCall{{
   504  		FuncName: "RunCommand",
   505  		Args: []interface{}{
   506  			listCmdArg,
   507  		},
   508  	}, {
   509  		FuncName: "ListUnits",
   510  	}, {
   511  		FuncName: "Close",
   512  	}, {
   513  		FuncName: "StartUnit",
   514  		Args: []interface{}{
   515  			s.name + ".service",
   516  			"fail",
   517  			(chan<- string)(s.ch),
   518  		},
   519  	}, {
   520  		FuncName: "Close",
   521  	}})
   522  }
   523  
   524  func (s *initSystemSuite) TestStartAlreadyRunning(c *gc.C) {
   525  	s.addService("jujud-machine-0", "active")
   526  	s.ch <- "done" // just in case
   527  	s.addListResponse()
   528  
   529  	err := s.service.Start()
   530  	c.Assert(err, jc.ErrorIsNil)
   531  
   532  	s.stub.CheckCallNames(c,
   533  		"RunCommand",
   534  		"ListUnits",
   535  		"Close",
   536  	)
   537  }
   538  
   539  func (s *initSystemSuite) TestStartNotInstalled(c *gc.C) {
   540  	s.ch <- "done" // just in case
   541  
   542  	err := s.service.Start()
   543  
   544  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   545  	s.stub.CheckCallNames(c, "RunCommand")
   546  }
   547  
   548  func (s *initSystemSuite) TestStop(c *gc.C) {
   549  	s.addService("jujud-machine-0", "active")
   550  	s.ch <- "done"
   551  
   552  	err := s.service.Stop()
   553  	c.Assert(err, jc.ErrorIsNil)
   554  
   555  	s.stub.CheckCalls(c, []testing.StubCall{{
   556  		FuncName: "ListUnits",
   557  	}, {
   558  		FuncName: "Close",
   559  	}, {
   560  		FuncName: "StopUnit",
   561  		Args: []interface{}{
   562  			s.name + ".service",
   563  			"fail",
   564  			(chan<- string)(s.ch),
   565  		},
   566  	}, {
   567  		FuncName: "Close",
   568  	}})
   569  }
   570  
   571  func (s *initSystemSuite) TestStopNotRunning(c *gc.C) {
   572  	s.addService("jujud-machine-0", "inactive")
   573  	s.ch <- "done" // just in case
   574  
   575  	err := s.service.Stop()
   576  	c.Assert(err, jc.ErrorIsNil)
   577  
   578  	s.stub.CheckCallNames(c, "ListUnits", "Close")
   579  }
   580  
   581  func (s *initSystemSuite) TestStopNotInstalled(c *gc.C) {
   582  	s.ch <- "done" // just in case
   583  
   584  	err := s.service.Stop()
   585  	c.Assert(err, jc.ErrorIsNil)
   586  
   587  	s.stub.CheckCallNames(c, "ListUnits", "Close")
   588  }
   589  
   590  func (s *initSystemSuite) TestRemove(c *gc.C) {
   591  	s.addService("jujud-machine-0", "inactive")
   592  	s.addListResponse()
   593  
   594  	err := s.service.Remove()
   595  	c.Assert(err, jc.ErrorIsNil)
   596  
   597  	s.stub.CheckCalls(c, []testing.StubCall{{
   598  		FuncName: "RunCommand",
   599  		Args: []interface{}{
   600  			listCmdArg,
   601  		},
   602  	}, {
   603  		FuncName: "DisableUnitFiles",
   604  		Args: []interface{}{
   605  			[]string{s.name + ".service"},
   606  			false,
   607  		},
   608  	}, {
   609  		FuncName: "Reload",
   610  	}, {
   611  		FuncName: "RemoveAll",
   612  		Args: []interface{}{
   613  			fmt.Sprintf("%s/init/%s", s.dataDir, s.name),
   614  		},
   615  	}, {
   616  		FuncName: "Close",
   617  	}})
   618  }
   619  
   620  func (s *initSystemSuite) TestRemoveNotInstalled(c *gc.C) {
   621  	err := s.service.Remove()
   622  	c.Assert(err, jc.ErrorIsNil)
   623  
   624  	s.stub.CheckCallNames(c, "RunCommand")
   625  }
   626  
   627  func (s *initSystemSuite) TestInstall(c *gc.C) {
   628  	err := s.service.Install()
   629  	c.Assert(err, jc.ErrorIsNil)
   630  
   631  	dirname := fmt.Sprintf("%s/init/%s", s.dataDir, s.name)
   632  	filename := fmt.Sprintf("%s/%s.service", dirname, s.name)
   633  	createFileOutput := s.stub.Calls()[2].Args[1] // gross
   634  	s.stub.CheckCalls(c, []testing.StubCall{{
   635  		FuncName: "RunCommand",
   636  		Args: []interface{}{
   637  			listCmdArg,
   638  		},
   639  	}, {
   640  		FuncName: "MkdirAll",
   641  		Args: []interface{}{
   642  			dirname,
   643  		},
   644  	}, {
   645  		FuncName: "CreateFile",
   646  		Args: []interface{}{
   647  			filename,
   648  			// The contents of the file will always pass this test. We are
   649  			// testing the sequence of commands. The output of CreateFile
   650  			// is tested by tests that call checkCreateFileCall.
   651  			createFileOutput,
   652  			os.FileMode(0644),
   653  		},
   654  	}, {
   655  		FuncName: "LinkUnitFiles",
   656  		Args: []interface{}{
   657  			[]string{filename},
   658  			false,
   659  			true,
   660  		},
   661  	}, {
   662  		FuncName: "Reload",
   663  	}, {
   664  		FuncName: "EnableUnitFiles",
   665  		Args: []interface{}{
   666  			[]string{filename},
   667  			false,
   668  			true,
   669  		},
   670  	}, {
   671  		FuncName: "Close",
   672  	}})
   673  	s.checkCreateFileCall(c, 2, filename, s.newConfStr(s.name), 0644)
   674  }
   675  
   676  func (s *initSystemSuite) TestInstallAlreadyInstalled(c *gc.C) {
   677  	s.addService("jujud-machine-0", "inactive")
   678  	s.addListResponse()
   679  	s.setConf(c, s.conf)
   680  
   681  	err := s.service.Install()
   682  	c.Assert(err, jc.ErrorIsNil)
   683  
   684  	s.stub.CheckCallNames(c,
   685  		"RunCommand",
   686  		"RunCommand",
   687  	)
   688  }
   689  
   690  func (s *initSystemSuite) TestInstallZombie(c *gc.C) {
   691  	s.addService("jujud-machine-0", "active")
   692  	s.addListResponse()
   693  	// We force the systemd API to return a slightly different conf.
   694  	// In this case we simply set a different Env value between the
   695  	// conf we are installing and the conf returned by the systemd API.
   696  	// This causes Service.Exists to return false.
   697  	conf := common.Conf{
   698  		Desc:      s.conf.Desc,
   699  		ExecStart: s.conf.ExecStart,
   700  		Env:       map[string]string{"a": "b"},
   701  	}
   702  	s.setConf(c, conf)
   703  	s.addListResponse()
   704  	s.ch <- "done"
   705  
   706  	conf.Env["a"] = "c"
   707  	service, err := systemd.NewService(s.name, conf, s.dataDir)
   708  	c.Assert(err, jc.ErrorIsNil)
   709  	err = service.Install()
   710  	c.Assert(err, jc.ErrorIsNil)
   711  
   712  	s.stub.CheckCallNames(c,
   713  		"RunCommand",
   714  		"RunCommand",
   715  		"ListUnits",
   716  		"Close",
   717  		"StopUnit",
   718  		"Close",
   719  		"RunCommand",
   720  		"DisableUnitFiles",
   721  		"Reload",
   722  		"RemoveAll",
   723  		"Close",
   724  		"MkdirAll",
   725  		"CreateFile",
   726  		"LinkUnitFiles",
   727  		"Reload",
   728  		"EnableUnitFiles",
   729  		"Close",
   730  	)
   731  	filename := fmt.Sprintf("%s/init/%s/%s.service", s.dataDir, s.name, s.name)
   732  	content := s.newConfStrEnv(s.name, `"a=c"`)
   733  	s.checkCreateFileCall(c, 12, filename, content, 0644)
   734  }
   735  
   736  func (s *initSystemSuite) TestInstallMultiline(c *gc.C) {
   737  	scriptPath := fmt.Sprintf("%s/init/%s/exec-start.sh", s.dataDir, s.name)
   738  	cmd := "a\nb\nc"
   739  	s.service.Service.Conf.ExecStart = scriptPath
   740  	s.service.Script = []byte(cmd)
   741  
   742  	err := s.service.Install()
   743  	c.Assert(err, jc.ErrorIsNil)
   744  
   745  	s.stub.CheckCallNames(c,
   746  		"RunCommand",
   747  		"MkdirAll",
   748  		"CreateFile",
   749  		"CreateFile",
   750  		"LinkUnitFiles",
   751  		"Reload",
   752  		"EnableUnitFiles",
   753  		"Close",
   754  	)
   755  	s.checkCreateFileCall(c, 2, scriptPath, cmd, 0755)
   756  	filename := fmt.Sprintf("%s/init/%s/%s.service", s.dataDir, s.name, s.name)
   757  	content := s.newConfStrCmd(s.name, scriptPath)
   758  	s.checkCreateFileCall(c, 3, filename, content, 0644)
   759  }
   760  
   761  func (s *initSystemSuite) TestInstallEmptyConf(c *gc.C) {
   762  	s.service.Service.Conf = common.Conf{}
   763  
   764  	err := s.service.Install()
   765  
   766  	c.Check(err, gc.ErrorMatches, `.*missing conf.*`)
   767  	s.stub.CheckCalls(c, nil)
   768  }
   769  
   770  func (s *initSystemSuite) TestInstallCommands(c *gc.C) {
   771  	name := "jujud-machine-0"
   772  	commands, err := s.service.InstallCommands()
   773  	c.Assert(err, jc.ErrorIsNil)
   774  
   775  	test := systemdtesting.WriteConfTest{
   776  		Service:  name,
   777  		DataDir:  s.dataDir,
   778  		Expected: s.newConfStr(name),
   779  	}
   780  	test.CheckCommands(c, commands)
   781  }
   782  
   783  func (s *initSystemSuite) TestInstallCommandsLogfile(c *gc.C) {
   784  	name := "jujud-machine-0"
   785  	s.conf.Logfile = "/var/log/juju/machine-0.log"
   786  	service := s.newService(c)
   787  	commands, err := service.InstallCommands()
   788  	c.Assert(err, jc.ErrorIsNil)
   789  
   790  	user, group := systemd.SyslogUserGroup()
   791  	test := systemdtesting.WriteConfTest{
   792  		Service: name,
   793  		DataDir: s.dataDir,
   794  		Expected: strings.Replace(
   795  			s.newConfStr(name),
   796  			"ExecStart=/var/lib/juju/bin/jujud machine-0",
   797  			"ExecStart=/var/lib/juju/init/jujud-machine-0/exec-start.sh",
   798  			-1),
   799  		Script: `
   800  # Set up logging.
   801  touch '/var/log/juju/machine-0.log'
   802  chown `[1:] + user + `:` + group + ` '/var/log/juju/machine-0.log'
   803  chmod 0600 '/var/log/juju/machine-0.log'
   804  exec >> '/var/log/juju/machine-0.log'
   805  exec 2>&1
   806  
   807  # Run the script.
   808  ` + jujud + " machine-0",
   809  	}
   810  	test.CheckCommands(c, commands)
   811  }
   812  
   813  func (s *initSystemSuite) TestInstallCommandsShutdown(c *gc.C) {
   814  	name := "juju-shutdown-job"
   815  	conf, err := service.ShutdownAfterConf("cloud-final")
   816  	c.Assert(err, jc.ErrorIsNil)
   817  	svc, err := systemd.NewService(name, conf, s.dataDir)
   818  	c.Assert(err, jc.ErrorIsNil)
   819  	commands, err := svc.InstallCommands()
   820  	c.Assert(err, jc.ErrorIsNil)
   821  
   822  	test := systemdtesting.WriteConfTest{
   823  		Service: name,
   824  		DataDir: s.dataDir,
   825  		Expected: `
   826  [Unit]
   827  Description=juju shutdown job
   828  After=syslog.target
   829  After=network.target
   830  After=systemd-user-sessions.service
   831  After=cloud-final
   832  
   833  [Service]
   834  ExecStart=/sbin/shutdown -h now
   835  ExecStopPost=/bin/systemctl disable juju-shutdown-job.service
   836  
   837  [Install]
   838  WantedBy=multi-user.target
   839  `[1:],
   840  	}
   841  	test.CheckCommands(c, commands)
   842  }
   843  
   844  func (s *initSystemSuite) TestInstallCommandsEmptyConf(c *gc.C) {
   845  	s.service.Service.Conf = common.Conf{}
   846  
   847  	_, err := s.service.InstallCommands()
   848  
   849  	c.Check(err, gc.ErrorMatches, `.*missing conf.*`)
   850  	s.stub.CheckCalls(c, nil)
   851  }
   852  
   853  func (s *initSystemSuite) TestStartCommands(c *gc.C) {
   854  	commands, err := s.service.StartCommands()
   855  	c.Assert(err, jc.ErrorIsNil)
   856  
   857  	c.Check(commands, jc.DeepEquals, []string{
   858  		"/bin/systemctl start jujud-machine-0.service",
   859  	})
   860  }