github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/mongo/mongo_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package mongo_test
     5  
     6  import (
     7  	"encoding/base64"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"path/filepath"
    14  	"regexp"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/juju/errors"
    20  	"github.com/juju/loggo"
    21  	"github.com/juju/os/series"
    22  	"github.com/juju/testing"
    23  	jc "github.com/juju/testing/checkers"
    24  	gc "gopkg.in/check.v1"
    25  
    26  	"github.com/juju/juju/controller"
    27  	"github.com/juju/juju/mongo"
    28  	"github.com/juju/juju/network"
    29  	"github.com/juju/juju/service/common"
    30  	svctesting "github.com/juju/juju/service/common/testing"
    31  	coretesting "github.com/juju/juju/testing"
    32  )
    33  
    34  type MongoSuite struct {
    35  	coretesting.BaseSuite
    36  	mongodConfigPath string
    37  	mongodPath       string
    38  	mongodVersion    mongo.Version
    39  
    40  	data *svctesting.FakeServiceData
    41  }
    42  
    43  var _ = gc.Suite(&MongoSuite{})
    44  
    45  var testInfo = struct {
    46  	StatePort    int
    47  	Cert         string
    48  	PrivateKey   string
    49  	SharedSecret string
    50  }{
    51  	StatePort:    25252,
    52  	Cert:         "foobar-cert",
    53  	PrivateKey:   "foobar-privkey",
    54  	SharedSecret: "foobar-sharedsecret",
    55  }
    56  
    57  var expectedArgs = struct {
    58  	MongoInstall []jc.SimpleMessage
    59  	YumBase      []string
    60  	AptGetBase   []string
    61  	Semanage     []string
    62  	Chcon        []string
    63  }{
    64  	MongoInstall: []jc.SimpleMessage{
    65  		{loggo.INFO, "Ensuring mongo server is running; data directory.*"},
    66  		{loggo.INFO, "Running: yum --assumeyes --debuglevel=1 install epel-release"},
    67  		{loggo.INFO, regexp.QuoteMeta("installing [mongodb-server]")},
    68  		{loggo.INFO, "Running: yum --assumeyes --debuglevel=1 install mongodb-server"},
    69  	},
    70  	YumBase: []string{
    71  		"--assumeyes",
    72  		"--debuglevel=1",
    73  		"install",
    74  	},
    75  	AptGetBase: []string{
    76  		"--option=Dpkg::Options::=--force-confold",
    77  		"--option=Dpkg::options::=--force-unsafe-io",
    78  		"--assume-yes",
    79  		"--quiet",
    80  		"install",
    81  	},
    82  	Semanage: []string{
    83  		"port",
    84  		"-a",
    85  		"-t",
    86  		"mongod_port_t",
    87  		"-p",
    88  		"tcp",
    89  		strconv.Itoa(controller.DefaultStatePort),
    90  	},
    91  	Chcon: []string{
    92  		"-R",
    93  		"-v",
    94  		"-t",
    95  		"mongod_var_lib_t",
    96  		"/var/lib/juju/",
    97  	},
    98  }
    99  
   100  func makeEnsureServerParams(dataDir string) mongo.EnsureServerParams {
   101  	return mongo.EnsureServerParams{
   102  		StatePort:    testInfo.StatePort,
   103  		Cert:         testInfo.Cert,
   104  		PrivateKey:   testInfo.PrivateKey,
   105  		SharedSecret: testInfo.SharedSecret,
   106  
   107  		DataDir: dataDir,
   108  	}
   109  }
   110  
   111  func (s *MongoSuite) makeConfigArgs(dataDir string) mongo.ConfigArgs {
   112  	return mongo.ConfigArgs{
   113  		DataDir:     dataDir,
   114  		DBDir:       dataDir,
   115  		MongoPath:   mongo.JujuMongod24Path,
   116  		Port:        1234,
   117  		OplogSizeMB: 1024,
   118  		WantNUMACtl: false,
   119  		Version:     s.mongodVersion,
   120  		IPv6:        true,
   121  	}
   122  }
   123  
   124  func (s *MongoSuite) SetUpTest(c *gc.C) {
   125  	s.BaseSuite.SetUpTest(c)
   126  
   127  	s.mongodVersion = mongo.Mongo24
   128  
   129  	testing.PatchExecutable(c, s, "mongod", "#!/bin/bash\n\nprintf %s 'db version v2.4.9'\n")
   130  	jujuMongodPath, err := exec.LookPath("mongod")
   131  	c.Assert(err, jc.ErrorIsNil)
   132  
   133  	s.PatchValue(&mongo.JujuMongod24Path, jujuMongodPath)
   134  	s.mongodPath = jujuMongodPath
   135  
   136  	// Patch "df" such that it always reports there's 1MB free.
   137  	s.PatchValue(mongo.AvailSpace, func(dir string) (float64, error) {
   138  		info, err := os.Stat(dir)
   139  		if err != nil {
   140  			return 0, err
   141  		}
   142  		if info.IsDir() {
   143  			return 1, nil
   144  
   145  		}
   146  		return 0, fmt.Errorf("not a directory")
   147  	})
   148  	s.PatchValue(mongo.SmallOplogSizeMB, 1)
   149  
   150  	testPath := c.MkDir()
   151  	s.mongodConfigPath = filepath.Join(testPath, "mongodConfig")
   152  	s.PatchValue(mongo.MongoConfigPath, s.mongodConfigPath)
   153  
   154  	s.data = svctesting.NewFakeServiceData()
   155  	mongo.PatchService(s.PatchValue, s.data)
   156  }
   157  
   158  func (s *MongoSuite) patchSeries(ser string) {
   159  	s.PatchValue(&series.MustHostSeries, func() string { return ser })
   160  }
   161  
   162  func (s *MongoSuite) TestJujuMongodPath(c *gc.C) {
   163  	obtained, err := mongo.Path(s.mongodVersion)
   164  	c.Check(err, jc.ErrorIsNil)
   165  	c.Check(obtained, gc.Matches, s.mongodPath)
   166  }
   167  
   168  func (s *MongoSuite) TestDefaultMongodPath(c *gc.C) {
   169  	s.PatchValue(&mongo.JujuMongod24Path, "/not/going/to/exist/mongod")
   170  	s.PatchEnvPathPrepend(filepath.Dir(s.mongodPath))
   171  
   172  	c.Logf("mongo version is %q", s.mongodVersion)
   173  	obtained, err := mongo.Path(s.mongodVersion)
   174  	c.Check(err, jc.ErrorIsNil)
   175  	c.Check(obtained, gc.Matches, s.mongodPath)
   176  }
   177  
   178  func (s *MongoSuite) TestMakeJournalDirs(c *gc.C) {
   179  	dir := c.MkDir()
   180  	err := mongo.MakeJournalDirs(dir)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  
   183  	testJournalDirs(dir, c)
   184  }
   185  
   186  func testJournalDirs(dir string, c *gc.C) {
   187  	journalDir := path.Join(dir, "journal")
   188  
   189  	c.Assert(journalDir, jc.IsDirectory)
   190  	info, err := os.Stat(filepath.Join(journalDir, "prealloc.0"))
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	size := int64(1024 * 1024)
   194  
   195  	c.Assert(info.Size(), gc.Equals, size)
   196  	info, err = os.Stat(filepath.Join(journalDir, "prealloc.1"))
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(info.Size(), gc.Equals, size)
   199  	info, err = os.Stat(filepath.Join(journalDir, "prealloc.2"))
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	c.Assert(info.Size(), gc.Equals, size)
   202  }
   203  
   204  func (s *MongoSuite) assertSSLKeyFile(c *gc.C, dataDir string) {
   205  	contents, err := ioutil.ReadFile(mongo.SSLKeyPath(dataDir))
   206  	c.Assert(err, jc.ErrorIsNil)
   207  	c.Assert(string(contents), gc.Equals, testInfo.Cert+"\n"+testInfo.PrivateKey)
   208  }
   209  
   210  func (s *MongoSuite) assertSharedSecretFile(c *gc.C, dataDir string) {
   211  	contents, err := ioutil.ReadFile(mongo.SharedSecretPath(dataDir))
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	c.Assert(string(contents), gc.Equals, testInfo.SharedSecret)
   214  }
   215  
   216  func (s *MongoSuite) assertMongoConfigFile(c *gc.C) {
   217  	contents, err := ioutil.ReadFile(s.mongodConfigPath)
   218  	c.Assert(err, jc.ErrorIsNil)
   219  	c.Assert(contents, jc.DeepEquals, []byte("ENABLE_MONGODB=no"))
   220  }
   221  
   222  func (s *MongoSuite) TestEnsureServer(c *gc.C) {
   223  	dataDir := s.testEnsureServerNUMACtl(c, false)
   224  
   225  	s.assertSSLKeyFile(c, dataDir)
   226  	s.assertSharedSecretFile(c, dataDir)
   227  	s.assertMongoConfigFile(c)
   228  
   229  	// make sure that we log the version of mongodb as we get ready to
   230  	// start it
   231  	tlog := c.GetTestLog()
   232  	any := `(.|\n)*`
   233  	start := "^" + any
   234  	tail := any + "$"
   235  	c.Assert(tlog, gc.Matches, start+`using mongod: .*/mongod --version: "db version v\d\.\d\.\d`+tail)
   236  }
   237  
   238  func (s *MongoSuite) TestEnsureServerServerExistsAndRunning(c *gc.C) {
   239  	dataDir := c.MkDir()
   240  
   241  	pm, err := coretesting.GetPackageManager()
   242  	c.Assert(err, jc.ErrorIsNil)
   243  
   244  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   245  
   246  	s.data.SetStatus(mongo.ServiceName, "running")
   247  	s.data.SetErrors(nil, nil, nil, errors.New("shouldn't be called"))
   248  
   249  	_, err = mongo.EnsureServer(makeEnsureServerParams(dataDir))
   250  	c.Assert(err, jc.ErrorIsNil)
   251  
   252  	// These should still be written out even if the service was installed.
   253  	s.assertSSLKeyFile(c, dataDir)
   254  	s.assertSharedSecretFile(c, dataDir)
   255  	s.assertMongoConfigFile(c)
   256  
   257  	c.Check(s.data.Installed(), gc.HasLen, 0)
   258  	s.data.CheckCallNames(c, "Running")
   259  }
   260  
   261  func (s *MongoSuite) TestEnsureServerSetsSysctlValues(c *gc.C) {
   262  	dataDir := c.MkDir()
   263  	dataFilePath := filepath.Join(dataDir, "mongoKernelTweaks")
   264  	dataFile, err := os.Create(dataFilePath)
   265  	c.Assert(err, jc.ErrorIsNil)
   266  	_, err = dataFile.WriteString("original value")
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	dataFile.Close()
   269  
   270  	pm, err := coretesting.GetPackageManager()
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   273  
   274  	s.data.SetStatus(mongo.ServiceName, "running")
   275  
   276  	contents, err := ioutil.ReadFile(dataFilePath)
   277  	c.Assert(err, jc.ErrorIsNil)
   278  	c.Assert(string(contents), gc.Equals, "original value")
   279  
   280  	_, err = mongo.SysctlEditableEnsureServer(makeEnsureServerParams(dataDir),
   281  		map[string]string{dataFilePath: "new value"})
   282  	c.Assert(err, jc.ErrorIsNil)
   283  
   284  	contents, err = ioutil.ReadFile(dataFilePath)
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	c.Assert(string(contents), gc.Equals, "new value")
   287  }
   288  
   289  func (s *MongoSuite) TestEnsureServerServerExistsNotRunningIsStarted(c *gc.C) {
   290  	dataDir := c.MkDir()
   291  
   292  	pm, err := coretesting.GetPackageManager()
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   295  
   296  	s.data.SetStatus(mongo.ServiceName, "installed")
   297  
   298  	_, err = mongo.EnsureServer(makeEnsureServerParams(dataDir))
   299  	c.Assert(err, jc.ErrorIsNil)
   300  
   301  	// These should still be written out even if the service was installed.
   302  	s.assertSSLKeyFile(c, dataDir)
   303  	s.assertSharedSecretFile(c, dataDir)
   304  	s.assertMongoConfigFile(c)
   305  
   306  	c.Check(s.data.Installed(), gc.HasLen, 0)
   307  	s.data.CheckCallNames(c, "Running", "Install", "Stop", "Start")
   308  }
   309  
   310  func (s *MongoSuite) TestEnsureServerServerExistsNotRunningStartError(c *gc.C) {
   311  	dataDir := c.MkDir()
   312  
   313  	pm, err := coretesting.GetPackageManager()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   316  
   317  	s.data.SetStatus(mongo.ServiceName, "installed")
   318  	failure := errors.New("won't start")
   319  	s.data.SetErrors(
   320  		nil,     // Running
   321  		nil,     // Install
   322  		nil,     // Stop
   323  		failure, // Start
   324  		nil,     // Stop
   325  		failure, // Start
   326  		nil,     // Stop
   327  		failure, // Start
   328  		nil,     // Stop
   329  		failure, // Start
   330  	)
   331  
   332  	_, err = mongo.EnsureServer(makeEnsureServerParams(dataDir))
   333  
   334  	c.Check(errors.Cause(err), gc.Equals, failure)
   335  	c.Check(s.data.Installed(), gc.HasLen, 0)
   336  	s.data.CheckCallNames(c,
   337  		"Running",
   338  		"Install",
   339  		"Stop",
   340  		"Start",
   341  		"Stop",
   342  		"Start",
   343  		"Stop",
   344  		"Start",
   345  		"Stop",
   346  		"Start",
   347  	)
   348  }
   349  
   350  func (s *MongoSuite) TestEnsureServerNUMACtl(c *gc.C) {
   351  	s.testEnsureServerNUMACtl(c, true)
   352  }
   353  
   354  func (s *MongoSuite) testEnsureServerNUMACtl(c *gc.C, setNUMAPolicy bool) string {
   355  	dataDir := c.MkDir()
   356  	dbDir := filepath.Join(dataDir, "db")
   357  
   358  	pm, err := coretesting.GetPackageManager()
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   361  
   362  	testParams := makeEnsureServerParams(dataDir)
   363  	testParams.SetNUMAControlPolicy = setNUMAPolicy
   364  	_, err = mongo.EnsureServer(testParams)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  
   367  	testJournalDirs(dbDir, c)
   368  
   369  	assertInstalled := func() {
   370  		installed := s.data.Installed()
   371  		c.Assert(installed, gc.HasLen, 1)
   372  		service := installed[0]
   373  		c.Assert(service.Name(), gc.Equals, "juju-db")
   374  		c.Assert(service.Conf().Desc, gc.Equals, "juju state database")
   375  		if setNUMAPolicy {
   376  			stripped := strings.Replace(service.Conf().ExtraScript, "\n", "", -1)
   377  			c.Assert(stripped, gc.Matches, `.* sysctl .*`)
   378  		} else {
   379  			c.Assert(service.Conf().ExtraScript, gc.Equals, "")
   380  		}
   381  		c.Assert(service.Conf().ExecStart, gc.Matches, `.*/mongod.*`)
   382  		c.Assert(service.Conf().Logfile, gc.Equals, "")
   383  	}
   384  	assertInstalled()
   385  	return dataDir
   386  }
   387  
   388  func (s *MongoSuite) TestInstallMongod(c *gc.C) {
   389  	type installs struct {
   390  		series string
   391  		cmd    [][]string
   392  	}
   393  
   394  	tests := []installs{
   395  		{"precise", [][]string{{"--target-release", "precise-updates/cloud-tools", "mongodb-server"}}},
   396  		{"trusty", [][]string{{"juju-mongodb"}}},
   397  		{"xenial", [][]string{{"juju-mongodb3.2"}, {"juju-mongo-tools3.2"}}},
   398  		{"bionic", [][]string{{"mongodb-server-core"}}},
   399  	}
   400  
   401  	testing.PatchExecutableAsEchoArgs(c, s, "add-apt-repository")
   402  	testing.PatchExecutableAsEchoArgs(c, s, "apt-get")
   403  	for _, test := range tests {
   404  		c.Logf("install for series %v", test.series)
   405  		dataDir := c.MkDir()
   406  		s.patchSeries(test.series)
   407  		_, err := mongo.EnsureServer(makeEnsureServerParams(dataDir))
   408  		c.Assert(err, jc.ErrorIsNil)
   409  
   410  		for _, cmd := range test.cmd {
   411  			match := append(expectedArgs.AptGetBase, cmd...)
   412  			testing.AssertEchoArgs(c, "apt-get", match...)
   413  		}
   414  	}
   415  }
   416  
   417  var fakeInstallScript = `#!/bin/bash
   418  if [ $# -lt 1 ]
   419  then
   420          echo "Install fail - not enough arguments"
   421          exit 1
   422  fi
   423  
   424  # The package name is the last argument
   425  package=${@: -1}
   426  echo $package >> %s
   427  
   428  if [ $package == "juju-mongodb" ]
   429  then
   430          echo "Installed successfully!"
   431          exit 0
   432  fi
   433  
   434  if [ $package == "mongodb-server" ]
   435  then
   436          echo "Installed successfully!"
   437          exit 0
   438  fi
   439  
   440  echo "Unable to locate package $package"
   441  exit 100
   442  `
   443  
   444  func (s *MongoSuite) TestInstallMongodFallsBack(c *gc.C) {
   445  	if runtime.GOOS == "windows" {
   446  		c.Skip("Skipping TestInstallMongodFallsBack as mongo is not installed on windows")
   447  	}
   448  
   449  	type installs struct {
   450  		series string
   451  		cmd    string
   452  	}
   453  
   454  	tests := []installs{
   455  		{"precise", "mongodb-server"},
   456  		{"trusty", "juju-mongodb"},
   457  		{"xenial", "juju-mongodb3.2"},
   458  		{"bionic", "mongodb-server-core"},
   459  	}
   460  
   461  	dataDir := c.MkDir()
   462  	outputFile := filepath.Join(dataDir, "apt-get-args")
   463  	testing.PatchExecutable(c, s, "apt-get", fmt.Sprintf(fakeInstallScript, outputFile))
   464  	for _, test := range tests {
   465  		c.Logf("Testing mongo install for series: %s", test.series)
   466  		s.patchSeries(test.series)
   467  		_, err := mongo.EnsureServer(makeEnsureServerParams(dataDir))
   468  		c.Assert(err, jc.ErrorIsNil)
   469  
   470  		args, err := ioutil.ReadFile(outputFile)
   471  		c.Assert(err, jc.ErrorIsNil)
   472  		c.Check(strings.TrimSpace(string(args)), gc.Equals, test.cmd)
   473  
   474  		err = os.Remove(outputFile)
   475  		c.Assert(err, jc.ErrorIsNil)
   476  	}
   477  }
   478  
   479  func (s *MongoSuite) TestInstallFailChconMongodCentOS(c *gc.C) {
   480  	returnCode := 1
   481  	execNameFail := "chcon"
   482  
   483  	exec := []string{"yum", "chcon"}
   484  
   485  	expectedResult := append(expectedArgs.MongoInstall, []jc.SimpleMessage{
   486  		{loggo.INFO, "running " + execNameFail + " .*"},
   487  		{loggo.ERROR, execNameFail + " failed to change file security context error exit status " + strconv.Itoa(returnCode)},
   488  		{loggo.ERROR, regexp.QuoteMeta("cannot install/upgrade mongod (will proceed anyway): exit status " + strconv.Itoa(returnCode))},
   489  	}...)
   490  	s.assertSuccessWithInstallStepFailCentOS(c, exec, execNameFail, returnCode, expectedResult)
   491  }
   492  
   493  func (s *MongoSuite) TestSemanageRuleExistsDoesNotFail(c *gc.C) {
   494  	// if the return code is 1 then the rule already exists and we do not fail
   495  	returnCode := 1
   496  	execNameFail := "semanage"
   497  
   498  	exec := []string{"yum", "chcon"}
   499  
   500  	expectedResult := append(expectedArgs.MongoInstall, []jc.SimpleMessage{
   501  		{loggo.INFO, "running chcon .*"},
   502  		{loggo.INFO, "running " + execNameFail + " .*"},
   503  	}...)
   504  
   505  	s.assertSuccessWithInstallStepFailCentOS(c, exec, execNameFail, returnCode, expectedResult)
   506  }
   507  
   508  func (s *MongoSuite) TestInstallFailSemanageMongodCentOS(c *gc.C) {
   509  	returnCode := 2
   510  	execNameFail := "semanage"
   511  
   512  	exec := []string{"yum", "chcon"}
   513  
   514  	expectedResult := append(expectedArgs.MongoInstall, []jc.SimpleMessage{
   515  		{loggo.INFO, "running chcon .*"},
   516  		{loggo.INFO, "running " + execNameFail + " .*"},
   517  		{loggo.ERROR, execNameFail + " failed to provide access on port " + strconv.Itoa(controller.DefaultStatePort) + " error exit status " + strconv.Itoa(returnCode)},
   518  		{loggo.ERROR, regexp.QuoteMeta("cannot install/upgrade mongod (will proceed anyway): exit status " + strconv.Itoa(returnCode))},
   519  	}...)
   520  	s.assertSuccessWithInstallStepFailCentOS(c, exec, execNameFail, returnCode, expectedResult)
   521  }
   522  
   523  func (s *MongoSuite) assertSuccessWithInstallStepFailCentOS(c *gc.C, exec []string, execNameFail string, returnCode int, expectedResult []jc.SimpleMessage) {
   524  	type installs struct {
   525  		series string
   526  		pkg    string
   527  	}
   528  	test := installs{
   529  		"centos7", "mongodb*",
   530  	}
   531  
   532  	for _, e := range exec {
   533  		testing.PatchExecutableAsEchoArgs(c, s, e)
   534  	}
   535  
   536  	testing.PatchExecutableThrowError(c, s, execNameFail, returnCode)
   537  
   538  	dataDir := c.MkDir()
   539  	s.patchSeries(test.series)
   540  
   541  	var tw loggo.TestWriter
   542  	c.Assert(loggo.RegisterWriter("mongosuite", &tw), jc.ErrorIsNil)
   543  	defer loggo.RemoveWriter("mongosuite")
   544  
   545  	_, err := mongo.EnsureServer(makeEnsureServerParams(dataDir))
   546  	c.Assert(err, jc.ErrorIsNil)
   547  	c.Assert(tw.Log(), jc.LogMatches, expectedResult)
   548  }
   549  
   550  func (s *MongoSuite) TestInstallSuccessMongodCentOS(c *gc.C) {
   551  	type installs struct {
   552  		series string
   553  		pkg    string
   554  	}
   555  	test := installs{
   556  		"centos7", "mongodb*",
   557  	}
   558  
   559  	testing.PatchExecutableAsEchoArgs(c, s, "yum")
   560  	testing.PatchExecutableAsEchoArgs(c, s, "chcon")
   561  	testing.PatchExecutableAsEchoArgs(c, s, "semanage")
   562  
   563  	dataDir := c.MkDir()
   564  	s.patchSeries(test.series)
   565  
   566  	_, err := mongo.EnsureServer(makeEnsureServerParams(dataDir))
   567  	c.Assert(err, jc.ErrorIsNil)
   568  
   569  	expected := append(expectedArgs.YumBase, "epel-release")
   570  
   571  	testing.AssertEchoArgs(c, "yum", expected...)
   572  
   573  	testing.AssertEchoArgs(c, "chcon", expectedArgs.Chcon...)
   574  
   575  	testing.AssertEchoArgs(c, "semanage", expectedArgs.Semanage...)
   576  }
   577  
   578  func (s *MongoSuite) TestMongoAptGetFails(c *gc.C) {
   579  	s.assertTestMongoGetFails(c, "trusty", "apt-get")
   580  }
   581  
   582  func (s *MongoSuite) TestMongoYumFails(c *gc.C) {
   583  	s.assertTestMongoGetFails(c, "centos7", "yum")
   584  }
   585  
   586  func (s *MongoSuite) assertTestMongoGetFails(c *gc.C, series string, packageManager string) {
   587  	s.patchSeries(series)
   588  
   589  	// Any exit code from apt-get that isn't 0 or 100 will be treated
   590  	// as unexpected, skipping the normal retry loop. failCmd causes
   591  	// the command to exit with 1.
   592  	binDir := c.MkDir()
   593  	s.PatchEnvPathPrepend(binDir)
   594  	failCmd(filepath.Join(binDir, packageManager))
   595  
   596  	// Set the mongodb service as installed but not running.
   597  	s.data.SetStatus(mongo.ServiceName, "installed")
   598  
   599  	var tw loggo.TestWriter
   600  	writer := loggo.NewMinimumLevelWriter(&tw, loggo.ERROR)
   601  	c.Assert(loggo.RegisterWriter("test-writer", writer), jc.ErrorIsNil)
   602  	defer loggo.RemoveWriter("test-writer")
   603  
   604  	dataDir := c.MkDir()
   605  	_, err := mongo.EnsureServer(makeEnsureServerParams(dataDir))
   606  
   607  	// Even though apt-get failed, EnsureServer should continue and
   608  	// not return the error - even though apt-get failed, the Juju
   609  	// mongodb package is most likely already installed.
   610  	// The error should be logged however.
   611  	c.Assert(err, jc.ErrorIsNil)
   612  
   613  	c.Check(tw.Log(), jc.LogMatches, []jc.SimpleMessage{
   614  		{loggo.ERROR, `packaging command failed: .+`},
   615  		{loggo.ERROR, `cannot install/upgrade mongod \(will proceed anyway\): packaging command failed`},
   616  	})
   617  
   618  	// Verify that EnsureServer continued and started the mongodb service.
   619  	c.Check(s.data.Installed(), gc.HasLen, 0)
   620  	s.data.CheckCallNames(c, "Running", "Install", "Stop", "Start")
   621  }
   622  
   623  func (s *MongoSuite) TestInstallMongodServiceExists(c *gc.C) {
   624  	pm, err := coretesting.GetPackageManager()
   625  	c.Assert(err, jc.ErrorIsNil)
   626  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   627  	if pm.PackageManager == "yum" {
   628  		testing.PatchExecutableAsEchoArgs(c, s, "chcon")
   629  		testing.PatchExecutableAsEchoArgs(c, s, "semanage")
   630  	}
   631  
   632  	dataDir := c.MkDir()
   633  
   634  	s.data.SetStatus(mongo.ServiceName, "running")
   635  	s.data.SetErrors(nil, nil, nil, errors.New("shouldn't be called"))
   636  
   637  	_, err = mongo.EnsureServer(makeEnsureServerParams(dataDir))
   638  	c.Assert(err, jc.ErrorIsNil)
   639  
   640  	c.Check(s.data.Installed(), gc.HasLen, 0)
   641  	s.data.CheckCallNames(c, "Running")
   642  }
   643  
   644  func (s *MongoSuite) TestNewServiceWithReplSet(c *gc.C) {
   645  	confArgs := s.makeConfigArgs(c.MkDir())
   646  	conf := mongo.NewConf(&confArgs)
   647  	c.Assert(strings.Contains(conf.ExecStart, "--replSet"), jc.IsTrue)
   648  }
   649  
   650  func (s *MongoSuite) TestNewServiceWithNumCtl(c *gc.C) {
   651  	args := s.makeConfigArgs(c.MkDir())
   652  	args.WantNUMACtl = true
   653  	conf := mongo.NewConf(&args)
   654  	c.Assert(conf.ExtraScript, gc.Not(gc.Matches), "")
   655  }
   656  
   657  func (s *MongoSuite) TestNewServiceWithIPv6(c *gc.C) {
   658  	args := s.makeConfigArgs(c.MkDir())
   659  	args.IPv6 = true
   660  	conf := mongo.NewConf(&args)
   661  	c.Assert(strings.Contains(conf.ExecStart, "--ipv6"), jc.IsTrue)
   662  }
   663  
   664  func (s *MongoSuite) TestNewServiceWithoutIPv6(c *gc.C) {
   665  	args := s.makeConfigArgs(c.MkDir())
   666  	args.IPv6 = false
   667  	conf := mongo.NewConf(&args)
   668  	c.Assert(strings.Contains(conf.ExecStart, "--ipv6"), jc.IsFalse)
   669  }
   670  
   671  func (s *MongoSuite) TestNewServiceWithJournal(c *gc.C) {
   672  	args := s.makeConfigArgs(c.MkDir())
   673  	args.Journal = true
   674  	conf := mongo.NewConf(&args)
   675  	c.Assert(conf.ExecStart, gc.Matches, `.* --journal.*`)
   676  }
   677  
   678  func (s *MongoSuite) TestRemoveService(c *gc.C) {
   679  	s.data.SetStatus(mongo.ServiceName, "running")
   680  
   681  	err := mongo.RemoveService()
   682  	c.Assert(err, jc.ErrorIsNil)
   683  
   684  	removed := s.data.Removed()
   685  	if !c.Check(removed, gc.HasLen, 1) {
   686  		c.Check(removed[0].Name(), gc.Equals, "juju-db-namespace")
   687  		c.Check(removed[0].Conf(), jc.DeepEquals, common.Conf{})
   688  	}
   689  	s.data.CheckCallNames(c, "Stop", "Remove")
   690  }
   691  
   692  func (s *MongoSuite) TestNoMongoDir(c *gc.C) {
   693  	// Make a non-existent directory that can nonetheless be
   694  	// created.
   695  	pm, err := coretesting.GetPackageManager()
   696  	c.Assert(err, jc.ErrorIsNil)
   697  	testing.PatchExecutableAsEchoArgs(c, s, pm.PackageManager)
   698  
   699  	dataDir := filepath.Join(c.MkDir(), "dir", "data")
   700  	_, err = mongo.EnsureServer(makeEnsureServerParams(dataDir))
   701  	c.Check(err, jc.ErrorIsNil)
   702  
   703  	_, err = os.Stat(filepath.Join(dataDir, "db"))
   704  	c.Assert(err, jc.ErrorIsNil)
   705  }
   706  
   707  func (s *MongoSuite) TestSelectPeerAddress(c *gc.C) {
   708  	addresses := []network.Address{{
   709  		Value: "127.0.0.1",
   710  		Type:  network.IPv4Address,
   711  		Scope: network.ScopeMachineLocal,
   712  	}, {
   713  		Value: "10.0.0.1",
   714  		Type:  network.IPv4Address,
   715  		Scope: network.ScopeCloudLocal,
   716  	}, {
   717  		Value: "8.8.8.8",
   718  		Type:  network.IPv4Address,
   719  		Scope: network.ScopePublic,
   720  	}}
   721  
   722  	address := mongo.SelectPeerAddress(addresses)
   723  	c.Assert(address, gc.Equals, "10.0.0.1")
   724  }
   725  
   726  func (s *MongoSuite) TestGenerateSharedSecret(c *gc.C) {
   727  	secret, err := mongo.GenerateSharedSecret()
   728  	c.Assert(err, jc.ErrorIsNil)
   729  	c.Assert(secret, gc.HasLen, 1024)
   730  	_, err = base64.StdEncoding.DecodeString(secret)
   731  	c.Assert(err, jc.ErrorIsNil)
   732  }
   733  
   734  func (s *MongoSuite) TestAddEpelInCentOS(c *gc.C) {
   735  	testing.PatchExecutableAsEchoArgs(c, s, "yum")
   736  
   737  	s.patchSeries("centos7")
   738  
   739  	testing.PatchExecutableAsEchoArgs(c, s, "chcon")
   740  	testing.PatchExecutableAsEchoArgs(c, s, "semanage")
   741  	testing.PatchExecutableAsEchoArgs(c, s, "yum-config-manager")
   742  
   743  	dataDir := c.MkDir()
   744  	_, err := mongo.EnsureServer(makeEnsureServerParams(dataDir))
   745  	c.Assert(err, jc.ErrorIsNil)
   746  
   747  	expectedEpelRelease := append(expectedArgs.YumBase, "epel-release")
   748  	testing.AssertEchoArgs(c, "yum", expectedEpelRelease...)
   749  
   750  	expectedMongodbServer := append(expectedArgs.YumBase, "mongodb-server")
   751  	testing.AssertEchoArgs(c, "yum", expectedMongodbServer...)
   752  
   753  	testing.AssertEchoArgs(c, "chcon", expectedArgs.Chcon...)
   754  
   755  	testing.AssertEchoArgs(c, "semanage", expectedArgs.Semanage...)
   756  }
   757  
   758  // failCmd creates an executable file at the given location that will do nothing
   759  // except return an error.
   760  func failCmd(path string) {
   761  	err := ioutil.WriteFile(path, []byte("#!/bin/bash --norc\nexit 1"), 0755)
   762  	if err != nil {
   763  		panic(err)
   764  	}
   765  }