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