github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/service/upstart/upstart_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upstart_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"testing"
    13  
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/symlink"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/service/common"
    20  	"github.com/juju/juju/service/upstart"
    21  	coretesting "github.com/juju/juju/testing"
    22  )
    23  
    24  func Test(t *testing.T) {
    25  	if runtime.GOOS == "windows" {
    26  		t.Skip("Skipping upstart tests on windows")
    27  	}
    28  	gc.TestingT(t)
    29  }
    30  
    31  type UpstartSuite struct {
    32  	coretesting.BaseSuite
    33  	testPath string
    34  	service  *upstart.Service
    35  	initDir  string
    36  }
    37  
    38  var _ = gc.Suite(&UpstartSuite{})
    39  
    40  func (s *UpstartSuite) SetUpTest(c *gc.C) {
    41  	s.testPath = c.MkDir()
    42  	s.initDir = c.MkDir()
    43  	s.PatchEnvPathPrepend(s.testPath)
    44  	s.PatchValue(&upstart.InitDir, s.initDir)
    45  	s.service = upstart.NewService(
    46  		"some-application",
    47  		common.Conf{
    48  			Desc:      "some service",
    49  			ExecStart: "/path/to/some-command",
    50  		},
    51  	)
    52  }
    53  
    54  var checkargs = `
    55  #!/bin/bash --norc
    56  if [ "$1" != "--system" ]; then
    57    exit 255
    58  fi
    59  if [ "$2" != "some-application" ]; then
    60    exit 255
    61  fi
    62  if [ "$3" != "" ]; then
    63    exit 255
    64  fi
    65  `[1:]
    66  
    67  func (s *UpstartSuite) MakeTool(c *gc.C, name, script string) {
    68  	path := filepath.Join(s.testPath, name)
    69  	err := ioutil.WriteFile(path, []byte(checkargs+script), 0755)
    70  	c.Assert(err, jc.ErrorIsNil)
    71  }
    72  
    73  func (s *UpstartSuite) StoppedStatus(c *gc.C) {
    74  	s.MakeTool(c, "status", `echo "some-application stop/waiting"`)
    75  }
    76  
    77  func (s *UpstartSuite) RunningStatusNoProcessID(c *gc.C) {
    78  	s.MakeTool(c, "status", `echo "some-application start/running"`)
    79  }
    80  
    81  func (s *UpstartSuite) RunningStatusWithProcessID(c *gc.C) {
    82  	s.MakeTool(c, "status", `echo "some-application start/running, process 123"`)
    83  }
    84  
    85  func (s *UpstartSuite) goodInstall(c *gc.C) {
    86  	s.MakeTool(c, "start", "exit 0")
    87  	err := s.service.Install()
    88  	c.Assert(err, jc.ErrorIsNil)
    89  }
    90  
    91  func (s *UpstartSuite) TestInstalled(c *gc.C) {
    92  	installed, err := s.service.Installed()
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	c.Check(installed, jc.IsFalse)
    95  
    96  	s.goodInstall(c)
    97  	installed, err = s.service.Installed()
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	c.Check(installed, jc.IsTrue)
   100  }
   101  
   102  func (s *UpstartSuite) TestExists(c *gc.C) {
   103  	// Setup creates the file, but it is empty.
   104  	exists, err := s.service.Exists()
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	c.Check(exists, jc.IsFalse)
   107  
   108  	s.goodInstall(c)
   109  	exists, err = s.service.Exists()
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	c.Check(exists, jc.IsTrue)
   112  }
   113  
   114  func (s *UpstartSuite) TestExistsNonEmpty(c *gc.C) {
   115  	s.goodInstall(c)
   116  	s.service.Service.Conf.ExecStart = "/path/to/other-command"
   117  
   118  	exists, err := s.service.Exists()
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Check(exists, jc.IsFalse)
   121  }
   122  
   123  func (s *UpstartSuite) TestRunning(c *gc.C) {
   124  	s.MakeTool(c, "status", "exit 1")
   125  	running, err := s.service.Running()
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	c.Check(running, jc.IsFalse)
   128  
   129  	s.MakeTool(c, "status", `echo "GIBBERISH NONSENSE"`)
   130  	running, err = s.service.Running()
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	c.Check(running, jc.IsFalse)
   133  
   134  	s.RunningStatusNoProcessID(c)
   135  	running, err = s.service.Running()
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Check(running, jc.IsTrue)
   138  
   139  	s.RunningStatusWithProcessID(c)
   140  	running, err = s.service.Running()
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	c.Check(running, jc.IsTrue)
   143  }
   144  
   145  func (s *UpstartSuite) TestStart(c *gc.C) {
   146  	s.RunningStatusWithProcessID(c)
   147  	s.MakeTool(c, "start", "exit 99")
   148  	c.Assert(s.service.Start(), jc.ErrorIsNil)
   149  	s.StoppedStatus(c)
   150  	c.Assert(s.service.Start(), gc.ErrorMatches, ".*exit status 99.*")
   151  	s.MakeTool(c, "start", "exit 0")
   152  	c.Assert(s.service.Start(), jc.ErrorIsNil)
   153  }
   154  
   155  func (s *UpstartSuite) TestStop(c *gc.C) {
   156  	s.StoppedStatus(c)
   157  	s.MakeTool(c, "stop", "exit 99")
   158  	c.Assert(s.service.Stop(), jc.ErrorIsNil)
   159  	s.RunningStatusWithProcessID(c)
   160  	c.Assert(s.service.Stop(), gc.ErrorMatches, ".*exit status 99.*")
   161  	s.MakeTool(c, "stop", "exit 0")
   162  	c.Assert(s.service.Stop(), jc.ErrorIsNil)
   163  }
   164  
   165  func (s *UpstartSuite) TestRemoveMissing(c *gc.C) {
   166  	err := s.service.Remove()
   167  
   168  	c.Check(err, jc.ErrorIsNil)
   169  }
   170  
   171  func (s *UpstartSuite) TestRemoveStopped(c *gc.C) {
   172  	s.goodInstall(c)
   173  	s.StoppedStatus(c)
   174  
   175  	err := s.service.Remove()
   176  	c.Assert(err, jc.ErrorIsNil)
   177  
   178  	filename := filepath.Join(upstart.InitDir, "some-application.conf")
   179  	_, err = os.Stat(filename)
   180  	c.Check(err, jc.Satisfies, os.IsNotExist)
   181  }
   182  
   183  func (s *UpstartSuite) TestStopRunning(c *gc.C) {
   184  	s.goodInstall(c)
   185  	s.RunningStatusWithProcessID(c)
   186  	s.MakeTool(c, "stop", "exit 99")
   187  	filename := filepath.Join(upstart.InitDir, "some-application.conf")
   188  	err := s.service.Stop()
   189  	c.Assert(err, gc.ErrorMatches, ".*exit status 99.*")
   190  
   191  	_, err = os.Stat(filename)
   192  	c.Assert(err, jc.ErrorIsNil)
   193  
   194  	s.MakeTool(c, "stop", "exit 0")
   195  	err = s.service.Stop()
   196  	c.Assert(err, jc.ErrorIsNil)
   197  }
   198  
   199  func (s *UpstartSuite) TestInstallErrors(c *gc.C) {
   200  	conf := common.Conf{}
   201  	check := func(msg string) {
   202  		c.Assert(s.service.Install(), gc.ErrorMatches, msg)
   203  		_, err := s.service.InstallCommands()
   204  		c.Assert(err, gc.ErrorMatches, msg)
   205  	}
   206  	s.service.Service.Conf = conf
   207  	s.service.Service.Name = ""
   208  	check("missing Name")
   209  	s.service.Service.Name = "some-application"
   210  	check("missing Desc")
   211  	s.service.Service.Conf.Desc = "this is an upstart service"
   212  	check("missing ExecStart")
   213  }
   214  
   215  const expectStart = `description "this is an upstart service"
   216  author "Juju Team <juju@lists.ubuntu.com>"
   217  start on runlevel [2345]
   218  stop on runlevel [!2345]
   219  respawn
   220  normal exit 0
   221  `
   222  
   223  func (s *UpstartSuite) dummyConf(c *gc.C) common.Conf {
   224  	return common.Conf{
   225  		Desc:      "this is an upstart service",
   226  		ExecStart: "/path/to/some-command x y z",
   227  	}
   228  }
   229  
   230  func (s *UpstartSuite) assertInstall(c *gc.C, conf common.Conf, expectEnd string) {
   231  	expectContent := expectStart + expectEnd
   232  	expectPath := filepath.Join(upstart.InitDir, "some-application.conf")
   233  
   234  	s.service.Service.Conf = conf
   235  	svc := s.service
   236  	cmds, err := s.service.InstallCommands()
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	c.Assert(cmds, gc.DeepEquals, []string{
   239  		"cat > " + expectPath + " << 'EOF'\n" + expectContent + "EOF\n",
   240  	})
   241  	cmds, err = s.service.StartCommands()
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	c.Assert(cmds, gc.DeepEquals, []string{
   244  		"start some-application",
   245  	})
   246  
   247  	s.MakeTool(c, "status", `echo "some-application stop/waiting"`)
   248  	s.MakeTool(c, "start", "exit 99")
   249  	err = svc.Install()
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	err = svc.Start()
   252  	c.Assert(err, gc.ErrorMatches, ".*exit status 99.*")
   253  
   254  	s.MakeTool(c, "start", "exit 0")
   255  	err = svc.Install()
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	err = svc.Start()
   258  	c.Assert(err, jc.ErrorIsNil)
   259  
   260  	content, err := ioutil.ReadFile(expectPath)
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(string(content), gc.Equals, expectContent)
   263  }
   264  
   265  func (s *UpstartSuite) TestInstallSimple(c *gc.C) {
   266  	conf := s.dummyConf(c)
   267  	s.assertInstall(c, conf, `
   268  
   269  script
   270  
   271  
   272    exec /path/to/some-command x y z
   273  end script
   274  `)
   275  }
   276  
   277  func (s *UpstartSuite) TestInstallExtraScript(c *gc.C) {
   278  	conf := s.dummyConf(c)
   279  	conf.ExtraScript = "extra lines of script"
   280  	s.assertInstall(c, conf, `
   281  
   282  script
   283  extra lines of script
   284  
   285    exec /path/to/some-command x y z
   286  end script
   287  `)
   288  }
   289  
   290  func (s *UpstartSuite) TestInstallLogfile(c *gc.C) {
   291  	conf := s.dummyConf(c)
   292  	conf.Logfile = "/some/output/path"
   293  	s.assertInstall(c, conf, `
   294  
   295  script
   296  
   297  
   298    # Ensure log files are properly protected
   299    touch /some/output/path
   300    chown syslog:syslog /some/output/path
   301    chmod 0600 /some/output/path
   302  
   303    exec /path/to/some-command x y z >> /some/output/path 2>&1
   304  end script
   305  `)
   306  }
   307  
   308  func (s *UpstartSuite) TestInstallEnv(c *gc.C) {
   309  	conf := s.dummyConf(c)
   310  	conf.Env = map[string]string{"FOO": "bar baz", "QUX": "ping pong"}
   311  	s.assertInstall(c, conf, `env FOO="bar baz"
   312  env QUX="ping pong"
   313  
   314  
   315  script
   316  
   317  
   318    exec /path/to/some-command x y z
   319  end script
   320  `)
   321  }
   322  
   323  func (s *UpstartSuite) TestInstallLimit(c *gc.C) {
   324  	conf := s.dummyConf(c)
   325  	conf.Limit = map[string]int{
   326  		"nofile": 65000,
   327  		"nproc":  20000,
   328  	}
   329  	s.assertInstall(c, conf, `
   330  limit nofile 65000 65000
   331  limit nproc 20000 20000
   332  
   333  script
   334  
   335  
   336    exec /path/to/some-command x y z
   337  end script
   338  `)
   339  }
   340  
   341  func (s *UpstartSuite) TestInstallAlreadyRunning(c *gc.C) {
   342  	pathTo := func(name string) string {
   343  		return filepath.Join(s.testPath, name)
   344  	}
   345  	s.MakeTool(c, "status-stopped", `echo "some-application stop/waiting"`)
   346  	s.MakeTool(c, "status-started", `echo "some-application start/running, process 123"`)
   347  	s.MakeTool(c, "stop", fmt.Sprintf(
   348  		"rm %s; ln -s %s %s",
   349  		pathTo("status"), pathTo("status-stopped"), pathTo("status"),
   350  	))
   351  	s.MakeTool(c, "start", fmt.Sprintf(
   352  		"rm %s; ln -s %s %s",
   353  		pathTo("status"), pathTo("status-started"), pathTo("status"),
   354  	))
   355  	err := symlink.New(pathTo("status-started"), pathTo("status"))
   356  	c.Assert(err, jc.ErrorIsNil)
   357  
   358  	svc := upstart.NewService("some-application", s.dummyConf(c))
   359  	err = svc.Install()
   360  	c.Assert(err, jc.ErrorIsNil)
   361  	installed, err := svc.Running()
   362  	c.Assert(err, jc.ErrorIsNil)
   363  	c.Check(installed, jc.IsTrue)
   364  }
   365  
   366  type IsRunningSuite struct {
   367  	coretesting.BaseSuite
   368  }
   369  
   370  var _ = gc.Suite(&IsRunningSuite{})
   371  
   372  const modeExecutable = 0500
   373  const modeNotExecutable = 0400
   374  
   375  // createInitctl creates a dummy initctl which returns the given
   376  // exitcode and patches the upstart package to use it.
   377  func (s *IsRunningSuite) createInitctl(c *gc.C, stderr string, exitcode int, mode os.FileMode) {
   378  	path := filepath.Join(c.MkDir(), "initctl")
   379  	var body string
   380  	if stderr != "" {
   381  		// Write to stderr.
   382  		body = ">&2 echo " + utils.ShQuote(stderr)
   383  	}
   384  	script := fmt.Sprintf(`
   385  #!/usr/bin/env bash
   386  %s
   387  exit %d
   388  `[1:], body, exitcode)
   389  	c.Logf(script)
   390  	err := ioutil.WriteFile(path, []byte(script), mode)
   391  	c.Assert(err, jc.ErrorIsNil)
   392  	s.PatchValue(upstart.InitctlPath, path)
   393  }
   394  
   395  func (s *IsRunningSuite) TestUpstartInstalled(c *gc.C) {
   396  	s.createInitctl(c, "", 0, modeExecutable)
   397  
   398  	isUpstart, err := upstart.IsRunning()
   399  	c.Assert(isUpstart, jc.IsTrue)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  }
   402  
   403  func (s *IsRunningSuite) TestUpstartNotInstalled(c *gc.C) {
   404  	s.PatchValue(upstart.InitctlPath, "/foo/bar/not-exist")
   405  
   406  	isUpstart, err := upstart.IsRunning()
   407  	c.Assert(isUpstart, jc.IsFalse)
   408  	c.Assert(err, jc.ErrorIsNil)
   409  }
   410  
   411  func (s *IsRunningSuite) TestUpstartInstalledButBroken(c *gc.C) {
   412  	const stderr = "<something broke>"
   413  	const errorCode = 99
   414  	s.createInitctl(c, stderr, errorCode, modeExecutable)
   415  
   416  	isUpstart, err := upstart.IsRunning()
   417  	c.Assert(isUpstart, jc.IsFalse)
   418  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf(".*exit status %d", errorCode))
   419  }
   420  
   421  func (s *IsRunningSuite) TestUpstartInstalledButNotRunning(c *gc.C) {
   422  	const stderr = `Name "com.ubuntu.Upstart" does not exist`
   423  	const errorCode = 1
   424  	s.createInitctl(c, stderr, errorCode, modeExecutable)
   425  
   426  	isUpstart, err := upstart.IsRunning()
   427  	c.Assert(isUpstart, jc.IsFalse)
   428  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf(".*exit status %d", errorCode))
   429  }
   430  
   431  func (s *IsRunningSuite) TestInitctlCantBeRun(c *gc.C) {
   432  	s.createInitctl(c, "", 0, modeNotExecutable)
   433  
   434  	isUpstart, err := upstart.IsRunning()
   435  	c.Assert(isUpstart, jc.IsFalse)
   436  	c.Assert(err, gc.ErrorMatches, ".+: permission denied")
   437  }