github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "context" 8 "encoding/base64" 9 "fmt" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "time" 14 15 "github.com/juju/clock" 16 "github.com/juju/clock/testclock" 17 "github.com/juju/errors" 18 "github.com/juju/testing" 19 jc "github.com/juju/testing/checkers" 20 "go.uber.org/mock/gomock" 21 gc "gopkg.in/check.v1" 22 23 "github.com/juju/juju/core/base" 24 "github.com/juju/juju/core/network" 25 "github.com/juju/juju/mongo" 26 "github.com/juju/juju/mongo/mongotest" 27 "github.com/juju/juju/packaging" 28 "github.com/juju/juju/service/common" 29 "github.com/juju/juju/service/snap" 30 coretesting "github.com/juju/juju/testing" 31 ) 32 33 type MongoSuite struct { 34 coretesting.BaseSuite 35 36 clock clock.Clock 37 mongodConfigPath string 38 39 mongoSnapService *mongotest.MockMongoSnapService 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 func makeEnsureServerParams(dataDir, configDir string) mongo.EnsureServerParams { 57 return mongo.EnsureServerParams{ 58 StatePort: testInfo.StatePort, 59 Cert: testInfo.Cert, 60 PrivateKey: testInfo.PrivateKey, 61 SharedSecret: testInfo.SharedSecret, 62 63 DataDir: dataDir, 64 ConfigDir: configDir, 65 JujuDBSnapChannel: "latest", 66 67 OplogSize: 1, 68 } 69 } 70 71 func (s *MongoSuite) SetUpTest(c *gc.C) { 72 s.BaseSuite.SetUpTest(c) 73 74 testing.PatchExecutable(c, s, "juju-db.mongod", "#!/bin/bash\n\nprintf %s 'db version v6.6.6'\n") 75 jujuMongodPath, err := exec.LookPath("juju-db.mongod") 76 c.Assert(err, jc.ErrorIsNil) 77 s.PatchValue(&mongo.JujuDbSnapMongodPath, jujuMongodPath) 78 79 // Patch "df" such that it always reports there's 1MB free. 80 s.PatchValue(mongo.AvailSpace, func(dir string) (float64, error) { 81 info, err := os.Stat(dir) 82 if err != nil { 83 return 0, err 84 } 85 if info.IsDir() { 86 return 1, nil 87 88 } 89 return 0, fmt.Errorf("not a directory") 90 }) 91 s.PatchValue(mongo.SmallOplogSizeMB, 1) 92 93 s.clock = testclock.NewClock(time.Now()) 94 } 95 96 func (s *MongoSuite) setupMocks(c *gc.C) *gomock.Controller { 97 ctrl := gomock.NewController(c) 98 99 s.mongoSnapService = mongotest.NewMockMongoSnapService(ctrl) 100 101 return ctrl 102 } 103 104 func (s *MongoSuite) expectInstallMongoSnap() { 105 mExp := s.mongoSnapService.EXPECT() 106 mExp.Name().Return("not-juju-db") 107 mExp.Install().Return(nil) 108 mExp.ConfigOverride().Return(nil) 109 mExp.Start().Return(nil).AnyTimes() 110 mExp.Running().Return(true, nil).AnyTimes() 111 112 s.PatchValue(mongo.NewSnapService, func(mainSnap, serviceName string, conf common.Conf, snapPath, configDir, channel string, confinementPolicy snap.ConfinementPolicy, backgroundServices []snap.BackgroundService, prerequisites []snap.Installable) (mongo.MongoSnapService, error) { 113 return s.mongoSnapService, nil 114 }) 115 } 116 117 func (s *MongoSuite) assertTLSKeyFile(c *gc.C, dataDir string) { 118 contents, err := os.ReadFile(mongo.SSLKeyPath(dataDir)) 119 c.Assert(err, jc.ErrorIsNil) 120 c.Assert(string(contents), gc.Equals, testInfo.Cert+"\n"+testInfo.PrivateKey) 121 } 122 123 func (s *MongoSuite) assertSharedSecretFile(c *gc.C, dataDir string) { 124 contents, err := os.ReadFile(mongo.SharedSecretPath(dataDir)) 125 c.Assert(err, jc.ErrorIsNil) 126 c.Assert(string(contents), gc.Equals, testInfo.SharedSecret) 127 } 128 129 func (s *MongoSuite) assertMongoConfigFile(c *gc.C, dataDir string, ipV6 bool) { 130 contents, err := os.ReadFile(s.mongodConfigPath) 131 c.Assert(err, jc.ErrorIsNil) 132 part1 := fmt.Sprintf(` 133 # WARNING 134 # autogenerated by juju on .* 135 # manual changes to this file are likely to be overwritten 136 auth = true 137 bind_ip_all = true 138 dbpath = %s/db`[1:], dataDir) 139 if ipV6 { 140 part1 += "\nipv6 = true" 141 } 142 143 part2 := fmt.Sprintf(` 144 journal = true 145 keyFile = %s/shared-secret 146 logpath = %s/logs/mongodb.log 147 oplogSize = 1 148 port = 25252 149 quiet = true 150 replSet = juju 151 slowms = 1000 152 storageEngine = wiredTiger 153 tlsCertificateKeyFile = %s/server.pem 154 tlsCertificateKeyFilePassword=ignored 155 tlsMode = requireTLS`, dataDir, dataDir, dataDir) 156 157 c.Assert(string(contents), gc.Matches, part1+part2) 158 } 159 160 func (s *MongoSuite) TestEnsureServerInstalled(c *gc.C) { 161 defer s.setupMocks(c).Finish() 162 s.expectInstallMongoSnap() 163 164 dataDir := s.assertEnsureServerIPv6(c, true) 165 166 s.assertTLSKeyFile(c, dataDir) 167 s.assertSharedSecretFile(c, dataDir) 168 s.assertMongoConfigFile(c, dataDir, true) 169 170 // make sure that we log the version of mongodb as we get ready to 171 // start it 172 tlog := c.GetTestLog() 173 anyExp := `(.|\n)*` 174 start := "^" + anyExp 175 tail := anyExp + "$" 176 c.Assert(tlog, gc.Matches, start+`using mongod: .*mongod --version:\sdb version v\d\.\d\.\d`+tail) 177 } 178 179 func (s *MongoSuite) TestEnsureServerInstalledNoIPv6(c *gc.C) { 180 defer s.setupMocks(c).Finish() 181 s.expectInstallMongoSnap() 182 183 dataDir := s.assertEnsureServerIPv6(c, false) 184 185 s.assertTLSKeyFile(c, dataDir) 186 s.assertSharedSecretFile(c, dataDir) 187 s.assertMongoConfigFile(c, dataDir, false) 188 } 189 190 func (s *MongoSuite) TestEnsureServerInstalledSetsSysctlValues(c *gc.C) { 191 defer s.setupMocks(c).Finish() 192 s.expectInstallMongoSnap() 193 194 dataDir := c.MkDir() 195 dataFilePath := filepath.Join(dataDir, "mongoKernelTweaks") 196 dataFile, err := os.Create(dataFilePath) 197 c.Assert(err, jc.ErrorIsNil) 198 _, err = dataFile.WriteString("original value") 199 c.Assert(err, jc.ErrorIsNil) 200 _ = dataFile.Close() 201 202 testing.PatchExecutableAsEchoArgs(c, s, "snap") 203 204 contents, err := os.ReadFile(dataFilePath) 205 c.Assert(err, jc.ErrorIsNil) 206 c.Assert(string(contents), gc.Equals, "original value") 207 208 configDir := c.MkDir() 209 err = mongo.SysctlEditableEnsureServer( 210 context.Background(), 211 makeEnsureServerParams(dataDir, configDir), 212 map[string]string{dataFilePath: "new value"}, 213 ) 214 c.Assert(err, jc.ErrorIsNil) 215 216 contents, err = os.ReadFile(dataFilePath) 217 c.Assert(err, jc.ErrorIsNil) 218 c.Assert(string(contents), gc.Equals, "new value") 219 } 220 221 func (s *MongoSuite) TestEnsureServerInstalledError(c *gc.C) { 222 defer s.setupMocks(c).Finish() 223 224 dataDir := c.MkDir() 225 configDir := c.MkDir() 226 227 testing.PatchExecutableAsEchoArgs(c, s, "snap") 228 229 failure := errors.New("boom") 230 s.PatchValue(mongo.InstallMongo, func(dep packaging.Dependency, b base.Base) error { 231 return failure 232 }) 233 234 err := mongo.EnsureServerInstalled(context.Background(), makeEnsureServerParams(dataDir, configDir)) 235 c.Assert(errors.Cause(err), gc.Equals, failure) 236 } 237 238 func (s *MongoSuite) assertEnsureServerIPv6(c *gc.C, ipv6 bool) string { 239 dataDir := c.MkDir() 240 configDir := c.MkDir() 241 s.mongodConfigPath = filepath.Join(dataDir, "juju-db.config") 242 243 testing.PatchExecutableAsEchoArgs(c, s, "snap") 244 245 s.PatchValue(mongo.SupportsIPv6, func() bool { 246 return ipv6 247 }) 248 testParams := makeEnsureServerParams(dataDir, configDir) 249 err := mongo.EnsureServerInstalled(context.Background(), testParams) 250 c.Assert(err, jc.ErrorIsNil) 251 return dataDir 252 } 253 254 func (s *MongoSuite) TestNoMongoDir(c *gc.C) { 255 defer s.setupMocks(c).Finish() 256 s.expectInstallMongoSnap() 257 258 // Make a non-existent directory that can nonetheless be 259 // created. 260 testing.PatchExecutableAsEchoArgs(c, s, "snap") 261 262 dataDir := filepath.Join(c.MkDir(), "dir", "data") 263 configDir := c.MkDir() 264 err := mongo.EnsureServerInstalled(context.Background(), makeEnsureServerParams(dataDir, configDir)) 265 c.Check(err, jc.ErrorIsNil) 266 267 _, err = os.Stat(filepath.Join(dataDir, "db")) 268 c.Assert(err, jc.ErrorIsNil) 269 } 270 271 func (s *MongoSuite) TestSelectPeerAddress(c *gc.C) { 272 addresses := network.ProviderAddresses{ 273 network.NewMachineAddress("126.0.0.1", network.WithScope(network.ScopeMachineLocal)).AsProviderAddress(), 274 network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), 275 network.NewMachineAddress("8.8.8.8", network.WithScope(network.ScopePublic)).AsProviderAddress(), 276 } 277 278 address := mongo.SelectPeerAddress(addresses) 279 c.Assert(address, gc.Equals, "10.0.0.1") 280 } 281 282 func (s *MongoSuite) TestGenerateSharedSecret(c *gc.C) { 283 secret, err := mongo.GenerateSharedSecret() 284 c.Assert(err, jc.ErrorIsNil) 285 c.Assert(secret, gc.HasLen, 1024) 286 _, err = base64.StdEncoding.DecodeString(secret) 287 c.Assert(err, jc.ErrorIsNil) 288 }