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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package service_test
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  
    13  	"github.com/juju/errors"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils/exec"
    16  	"github.com/juju/utils/featureflag"
    17  	jujuos "github.com/juju/utils/os"
    18  	"github.com/juju/utils/series"
    19  	"github.com/juju/version"
    20  	gc "gopkg.in/check.v1"
    21  
    22  	"github.com/juju/juju/feature"
    23  	"github.com/juju/juju/juju/osenv"
    24  	"github.com/juju/juju/service"
    25  	"github.com/juju/juju/service/common"
    26  	"github.com/juju/juju/service/systemd"
    27  	"github.com/juju/juju/service/upstart"
    28  	"github.com/juju/juju/service/windows"
    29  )
    30  
    31  var maybeSystemd = service.InitSystemSystemd
    32  
    33  func init() {
    34  	if featureflag.Enabled(feature.LegacyUpstart) {
    35  		maybeSystemd = service.InitSystemUpstart
    36  	}
    37  }
    38  
    39  const unknownExecutable = "/sbin/unknown/init/system"
    40  
    41  type discoveryTest struct {
    42  	os       jujuos.OSType
    43  	series   string
    44  	expected string
    45  }
    46  
    47  func (dt discoveryTest) version() version.Binary {
    48  	return version.Binary{
    49  		Series: dt.series,
    50  	}
    51  }
    52  
    53  func (dt discoveryTest) log(c *gc.C) {
    54  	c.Logf(" - testing {%q, %q}...", dt.os, dt.series)
    55  }
    56  
    57  func (dt discoveryTest) disableLocalDiscovery(c *gc.C, s *discoverySuite) {
    58  	s.PatchLocalDiscoveryDisable()
    59  }
    60  
    61  func (dt discoveryTest) disableVersionDiscovery(s *discoverySuite) {
    62  	dt.os = jujuos.Unknown
    63  	dt.setVersion(s)
    64  }
    65  
    66  func (dt discoveryTest) setLocal(c *gc.C, s *discoverySuite) {
    67  	s.PatchLocalDiscoveryNoMatch(dt.expected)
    68  }
    69  
    70  func (dt discoveryTest) setVersion(s *discoverySuite) version.Binary {
    71  	vers := dt.version()
    72  	s.PatchSeries(vers.Series)
    73  	return vers
    74  }
    75  
    76  func (dt discoveryTest) checkService(c *gc.C, svc service.Service, err error, name string, conf common.Conf) {
    77  	if dt.expected == "" {
    78  		c.Check(err, jc.Satisfies, errors.IsNotFound)
    79  		return
    80  	}
    81  
    82  	// Check the success case.
    83  	if !c.Check(err, jc.ErrorIsNil) {
    84  		return
    85  	}
    86  	switch dt.expected {
    87  	case service.InitSystemUpstart:
    88  		c.Check(svc, gc.FitsTypeOf, &upstart.Service{})
    89  	case service.InitSystemSystemd:
    90  		c.Check(svc, gc.FitsTypeOf, &systemd.Service{})
    91  	case service.InitSystemWindows:
    92  		c.Check(svc, gc.FitsTypeOf, &windows.Service{})
    93  	default:
    94  		c.Errorf("unknown expected init system %q", dt.expected)
    95  		return
    96  	}
    97  	if svc == nil {
    98  		return
    99  	}
   100  
   101  	c.Check(svc.Name(), gc.Equals, name)
   102  	c.Check(svc.Conf(), jc.DeepEquals, conf)
   103  }
   104  
   105  func (dt discoveryTest) checkInitSystem(c *gc.C, name string, err error) {
   106  	if dt.expected == "" {
   107  		if !c.Check(err, jc.Satisfies, errors.IsNotFound) {
   108  			c.Logf("found init system %q", name)
   109  		}
   110  	} else {
   111  		c.Check(err, jc.ErrorIsNil)
   112  		c.Check(name, gc.Equals, dt.expected)
   113  	}
   114  }
   115  
   116  var discoveryTests = []discoveryTest{{
   117  	os:       jujuos.Windows,
   118  	series:   "win2012",
   119  	expected: service.InitSystemWindows,
   120  }, {
   121  	os:       jujuos.Ubuntu,
   122  	series:   "oneiric",
   123  	expected: "",
   124  }, {
   125  	os:       jujuos.Ubuntu,
   126  	series:   "precise",
   127  	expected: service.InitSystemUpstart,
   128  }, {
   129  	os:       jujuos.Ubuntu,
   130  	series:   "utopic",
   131  	expected: service.InitSystemUpstart,
   132  }, {
   133  	os:       jujuos.Ubuntu,
   134  	series:   "vivid",
   135  	expected: maybeSystemd,
   136  }, {
   137  	os:       jujuos.CentOS,
   138  	series:   "centos7",
   139  	expected: service.InitSystemSystemd,
   140  }, {
   141  	os:       jujuos.Unknown,
   142  	expected: "",
   143  }}
   144  
   145  type discoverySuite struct {
   146  	service.BaseSuite
   147  
   148  	name string
   149  	conf common.Conf
   150  }
   151  
   152  var _ = gc.Suite(&discoverySuite{})
   153  
   154  func (s *discoverySuite) SetUpTest(c *gc.C) {
   155  	s.BaseSuite.SetUpTest(c)
   156  
   157  	s.name = "a-application"
   158  	s.conf = common.Conf{
   159  		Desc:      "some service",
   160  		ExecStart: "/path/to/some-command",
   161  	}
   162  }
   163  
   164  func (s *discoverySuite) unsetLegacyUpstart(c *gc.C) {
   165  	err := os.Setenv(osenv.JujuFeatureFlagEnvKey, "")
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey)
   168  }
   169  
   170  func (s *discoverySuite) setLegacyUpstart(c *gc.C) {
   171  	err := os.Setenv(osenv.JujuFeatureFlagEnvKey, feature.LegacyUpstart)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey)
   174  }
   175  
   176  func (s *discoverySuite) TestDiscoverServiceLocalHost(c *gc.C) {
   177  	var localInitSystem string
   178  	var err error
   179  	switch runtime.GOOS {
   180  	case "windows":
   181  		localInitSystem = service.InitSystemWindows
   182  	case "linux":
   183  		localInitSystem, err = service.VersionInitSystem(series.HostSeries())
   184  	}
   185  	c.Assert(err, gc.IsNil)
   186  
   187  	test := discoveryTest{
   188  		os:       jujuos.HostOS(),
   189  		series:   series.HostSeries(),
   190  		expected: localInitSystem,
   191  	}
   192  	test.disableVersionDiscovery(s)
   193  
   194  	svc, err := service.DiscoverService(s.name, s.conf)
   195  	c.Assert(err, jc.ErrorIsNil)
   196  
   197  	test.checkService(c, svc, err, s.name, s.conf)
   198  }
   199  
   200  func (s *discoverySuite) TestDiscoverServiceVersionFallback(c *gc.C) {
   201  	for _, test := range discoveryTests {
   202  		test.log(c)
   203  
   204  		test.disableLocalDiscovery(c, s)
   205  		test.setVersion(s)
   206  
   207  		svc, err := service.DiscoverService(s.name, s.conf)
   208  
   209  		test.checkService(c, svc, err, s.name, s.conf)
   210  	}
   211  }
   212  
   213  func (s *discoverySuite) TestVersionInitSystem(c *gc.C) {
   214  	for _, test := range discoveryTests {
   215  		test.log(c)
   216  		initSystem, err := service.VersionInitSystem(test.series)
   217  		test.checkInitSystem(c, initSystem, err)
   218  	}
   219  }
   220  
   221  func (s *discoverySuite) TestVersionInitSystemLegacyUpstart(c *gc.C) {
   222  	s.setLegacyUpstart(c)
   223  	test := discoveryTest{
   224  		os:       jujuos.Ubuntu,
   225  		series:   "vivid",
   226  		expected: service.InitSystemUpstart,
   227  	}
   228  	vers := test.setVersion(s)
   229  
   230  	initSystem, err := service.VersionInitSystem(vers.Series)
   231  
   232  	test.checkInitSystem(c, initSystem, err)
   233  }
   234  
   235  func (s *discoverySuite) TestVersionInitSystemNoLegacyUpstart(c *gc.C) {
   236  	s.unsetLegacyUpstart(c)
   237  	test := discoveryTest{
   238  		os:       jujuos.Ubuntu,
   239  		series:   "vivid",
   240  		expected: service.InitSystemSystemd,
   241  	}
   242  	vers := test.setVersion(s)
   243  
   244  	initSystem, err := service.VersionInitSystem(vers.Series)
   245  
   246  	test.checkInitSystem(c, initSystem, err)
   247  }
   248  
   249  func (s *discoverySuite) TestDiscoverLocalInitSystemMatchFirst(c *gc.C) {
   250  	s.PatchLocalDiscovery(
   251  		service.NewDiscoveryCheck("initA", true, nil),
   252  		service.NewDiscoveryCheck("initB", false, nil),
   253  	)
   254  
   255  	name, err := service.DiscoverLocalInitSystem()
   256  	c.Assert(err, jc.ErrorIsNil)
   257  
   258  	c.Check(name, gc.Equals, "initA")
   259  }
   260  
   261  func (s *discoverySuite) TestDiscoverLocalInitSystemErrorFirst(c *gc.C) {
   262  	failure := errors.New("<failed>")
   263  	s.PatchLocalDiscovery(
   264  		service.NewDiscoveryCheck("initA", false, failure),
   265  		service.NewDiscoveryCheck("initB", true, nil),
   266  	)
   267  
   268  	name, err := service.DiscoverLocalInitSystem()
   269  	c.Assert(err, jc.ErrorIsNil)
   270  
   271  	c.Check(name, gc.Equals, "initB")
   272  }
   273  
   274  func (s *discoverySuite) TestDiscoverLocalInitSystemMatchFirstError(c *gc.C) {
   275  	failure := errors.New("<failed>")
   276  	s.PatchLocalDiscovery(
   277  		service.NewDiscoveryCheck("initA", true, failure),
   278  		service.NewDiscoveryCheck("initB", false, nil),
   279  	)
   280  
   281  	name, err := service.DiscoverLocalInitSystem()
   282  	c.Assert(err, jc.ErrorIsNil)
   283  
   284  	c.Check(name, gc.Equals, "initA")
   285  }
   286  
   287  func (s *discoverySuite) TestDiscoverLocalInitSystemMatchSecond(c *gc.C) {
   288  	s.PatchLocalDiscovery(
   289  		service.NewDiscoveryCheck("initA", false, nil),
   290  		service.NewDiscoveryCheck("initB", true, nil),
   291  	)
   292  
   293  	name, err := service.DiscoverLocalInitSystem()
   294  	c.Assert(err, jc.ErrorIsNil)
   295  
   296  	c.Check(name, gc.Equals, "initB")
   297  }
   298  
   299  func (s *discoverySuite) TestDiscoverLocalInitSystemMatchNone(c *gc.C) {
   300  	s.PatchLocalDiscovery(
   301  		service.NewDiscoveryCheck("initA", false, nil),
   302  		service.NewDiscoveryCheck("initB", false, nil),
   303  	)
   304  
   305  	_, err := service.DiscoverLocalInitSystem()
   306  
   307  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   308  }
   309  
   310  func (s *discoverySuite) TestDiscoverLocalInitSystemErrorMixed(c *gc.C) {
   311  	failure := errors.New("<failed>")
   312  	s.PatchLocalDiscovery(
   313  		service.NewDiscoveryCheck("initA", false, failure),
   314  		service.NewDiscoveryCheck("initB", false, nil),
   315  	)
   316  
   317  	_, err := service.DiscoverLocalInitSystem()
   318  
   319  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   320  }
   321  
   322  func (s *discoverySuite) TestDiscoverLocalInitSystemErrorAll(c *gc.C) {
   323  	failureA := errors.New("<failed A>")
   324  	failureB := errors.New("<failed B>")
   325  	s.PatchLocalDiscovery(
   326  		service.NewDiscoveryCheck("initA", false, failureA),
   327  		service.NewDiscoveryCheck("initB", false, failureB),
   328  	)
   329  
   330  	_, err := service.DiscoverLocalInitSystem()
   331  
   332  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   333  }
   334  
   335  func (s *discoverySuite) TestDiscoverInitSystemScriptBash(c *gc.C) {
   336  	if runtime.GOOS == "windows" {
   337  		c.Skip("not supported on windows")
   338  	}
   339  
   340  	script, filename := s.newDiscoverInitSystemScript(c)
   341  	script += filename
   342  	response, err := exec.RunCommands(exec.RunParams{
   343  		Commands: script,
   344  	})
   345  	c.Assert(err, jc.ErrorIsNil)
   346  
   347  	initSystem, err := service.DiscoverInitSystem()
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	c.Check(response.Code, gc.Equals, 0)
   350  	c.Check(string(response.Stdout), gc.Equals, initSystem)
   351  	c.Check(string(response.Stderr), gc.Equals, "")
   352  }
   353  
   354  func (s *discoverySuite) TestDiscoverInitSystemScriptPosix(c *gc.C) {
   355  	if runtime.GOOS == "windows" {
   356  		c.Skip("not supported on windows")
   357  	}
   358  
   359  	script, filename := s.newDiscoverInitSystemScript(c)
   360  	script += "sh " + filename
   361  	response, err := exec.RunCommands(exec.RunParams{
   362  		Commands: script,
   363  	})
   364  	c.Assert(err, jc.ErrorIsNil)
   365  
   366  	initSystem, err := service.DiscoverInitSystem()
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	c.Check(response.Code, gc.Equals, 0)
   369  	c.Check(string(response.Stdout), gc.Equals, initSystem)
   370  	c.Check(string(response.Stderr), gc.Equals, "")
   371  }
   372  
   373  func (s *discoverySuite) writeScript(c *gc.C, name, script string) (string, string) {
   374  	filename := filepath.Join(c.MkDir(), name)
   375  	commands := []string{
   376  		fmt.Sprintf(`
   377  cat > %s << 'EOF'
   378  %s
   379  EOF`[1:], filename, script),
   380  		"chmod 0755 " + filename,
   381  	}
   382  	writeScript := strings.Join(commands, "\n") + "\n"
   383  	return writeScript, filename
   384  }
   385  
   386  func (s *discoverySuite) newDiscoverInitSystemScript(c *gc.C) (string, string) {
   387  	script := service.DiscoverInitSystemScript()
   388  	return s.writeScript(c, "discover_init_system.sh", script)
   389  }
   390  
   391  func (s *discoverySuite) TestNewShellSelectCommandBash(c *gc.C) {
   392  	if runtime.GOOS == "windows" {
   393  		c.Skip("not supported on windows")
   394  	}
   395  
   396  	discoveryScript := service.DiscoverInitSystemScript()
   397  	handler := func(initSystem string) (string, bool) {
   398  		return "echo -n " + initSystem, true
   399  	}
   400  	script := "init_system=$(" + discoveryScript + ")\n"
   401  	// The script will fail with exit 1 if it cannot match in init system.
   402  	script += service.NewShellSelectCommand("init_system", "exit 1", handler)
   403  	response, err := exec.RunCommands(exec.RunParams{
   404  		Commands: script,
   405  	})
   406  	c.Assert(err, jc.ErrorIsNil)
   407  
   408  	initSystem, err := service.DiscoverInitSystem()
   409  	c.Assert(err, jc.ErrorIsNil)
   410  	c.Check(response.Code, gc.Equals, 0)
   411  	c.Check(string(response.Stdout), gc.Equals, initSystem)
   412  	c.Check(string(response.Stderr), gc.Equals, "")
   413  }
   414  
   415  func (s *discoverySuite) TestNewShellSelectCommandPosix(c *gc.C) {
   416  	if runtime.GOOS == "windows" {
   417  		c.Skip("not supported on windows")
   418  	}
   419  
   420  	discoveryScript := service.DiscoverInitSystemScript()
   421  	handler := func(initSystem string) (string, bool) {
   422  		return "echo -n " + initSystem, true
   423  	}
   424  	script := "init_system=$(" + discoveryScript + ")\n"
   425  	// The script will fail with exit 1 if it cannot match in init system.
   426  	script += service.NewShellSelectCommand("init_system", "exit 1", handler)
   427  	commands, filename := s.writeScript(c, "test_shell_select.sh", script)
   428  	commands += "sh " + filename
   429  	response, err := exec.RunCommands(exec.RunParams{
   430  		Commands: script,
   431  	})
   432  	c.Assert(err, jc.ErrorIsNil)
   433  
   434  	initSystem, err := service.DiscoverInitSystem()
   435  	c.Assert(err, jc.ErrorIsNil)
   436  	c.Check(response.Code, gc.Equals, 0)
   437  	c.Check(string(response.Stdout), gc.Equals, initSystem)
   438  	c.Check(string(response.Stderr), gc.Equals, "")
   439  }