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