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