github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/mongo/mongo.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package mongo 5 6 import ( 7 "crypto/rand" 8 "encoding/base64" 9 "fmt" 10 "net" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "strconv" 16 "strings" 17 18 "github.com/juju/errors" 19 "github.com/juju/loggo" 20 "github.com/juju/os/series" 21 "github.com/juju/packaging/config" 22 "github.com/juju/packaging/manager" 23 "github.com/juju/replicaset" 24 "github.com/juju/utils" 25 "github.com/juju/utils/featureflag" 26 "gopkg.in/mgo.v2" 27 28 "github.com/juju/juju/controller" 29 "github.com/juju/juju/feature" 30 "github.com/juju/juju/network" 31 "github.com/juju/juju/service" 32 "github.com/juju/juju/service/common" 33 "github.com/juju/juju/service/snap" 34 ) 35 36 var ( 37 logger = loggo.GetLogger("juju.mongo") 38 mongoConfigPath = "/etc/default/mongodb" 39 40 // JujuMongod24Path holds the default path to the legacy Juju 41 // mongod. 42 JujuMongod24Path = "/usr/lib/juju/bin/mongod" 43 44 // JujuMongod32Path holds the default path to juju-mongodb3.2 45 JujuMongod32Path = "/usr/lib/juju/mongo3.2/bin/mongod" 46 47 // MongodSystemPath is actually just the system path 48 MongodSystemPath = "/usr/bin/mongod" 49 50 // This is NUMACTL package name for apt-get 51 numaCtlPkg = "numactl" 52 53 // mininmumSystemMongoVersion is the minimum version we would allow to be used from /usr/bin/mongod. 54 minimumSystemMongoVersion = Version{Major: 3, Minor: 4} 55 ) 56 57 // StorageEngine represents the storage used by mongo. 58 type StorageEngine string 59 60 const ( 61 // JujuMongoPackage is the mongo package Juju uses when 62 // installing mongo. 63 JujuMongoPackage = "juju-mongodb3.2" 64 65 // JujuDbSnap is the snap of MongoDB that Juju uses. 66 JujuDbSnap = "juju-db" 67 68 // JujuDbSnapMongodPath is the path that the juju-db snap 69 // makes mongod available at 70 JujuDbSnapMongodPath = "/snap/bin/juju-db.mongod" 71 72 // JujuMongoToolsPackage is the mongo package Juju uses when 73 // installing mongo tools to get mongodump etc. 74 JujuMongoToolsPackage = "juju-mongo-tools3.2" 75 76 // MMAPV1 is the default storage engine in mongo db up to 3.x 77 MMAPV1 StorageEngine = "mmapv1" 78 79 // WiredTiger is a storage type introduced in 3 80 WiredTiger StorageEngine = "wiredTiger" 81 82 // Upgrading is a special case where mongo is being upgraded. 83 Upgrading StorageEngine = "Upgrading" 84 ) 85 86 // Version represents the major.minor version of the running mongo. 87 type Version struct { 88 Major int 89 Minor int 90 Patch string // supports variants like 1-alpha 91 StorageEngine StorageEngine 92 } 93 94 // NewerThan will return 1 if the passed version is older than 95 // v, 0 if they are equal (or ver is a special case such as 96 // Upgrading and -1 if ver is newer. 97 func (v Version) NewerThan(ver Version) int { 98 if v == MongoUpgrade || ver == MongoUpgrade { 99 return 0 100 } 101 if v.Major > ver.Major { 102 return 1 103 } 104 if v.Major < ver.Major { 105 return -1 106 } 107 if v.Minor > ver.Minor { 108 return 1 109 } 110 if v.Minor < ver.Minor { 111 return -1 112 } 113 return 0 114 } 115 116 // NewVersion returns a mongo Version parsing the passed version string 117 // or error if not possible. 118 // A valid version string is of the form: 119 // 1.2.patch/storage 120 // major and minor are positive integers, patch is a string containing 121 // any ascii character except / and storage is one of the above defined 122 // StorageEngine. Only major is mandatory. 123 // An alternative valid string is 0.0/Upgrading which represents that 124 // mongo is being upgraded. 125 func NewVersion(v string) (Version, error) { 126 version := Version{} 127 if v == "" { 128 return Mongo24, nil 129 } 130 131 parts := strings.SplitN(v, "/", 2) 132 switch len(parts) { 133 case 0: 134 return Version{}, errors.New("invalid version string") 135 case 1: 136 version.StorageEngine = MMAPV1 137 case 2: 138 switch StorageEngine(parts[1]) { 139 case MMAPV1: 140 version.StorageEngine = MMAPV1 141 case WiredTiger: 142 version.StorageEngine = WiredTiger 143 case Upgrading: 144 version.StorageEngine = Upgrading 145 } 146 } 147 vParts := strings.SplitN(parts[0], ".", 3) 148 149 if len(vParts) >= 1 { 150 i, err := strconv.Atoi(vParts[0]) 151 if err != nil { 152 return Version{}, errors.Annotate(err, "Invalid version string, major is not an int") 153 } 154 version.Major = i 155 } 156 if len(vParts) >= 2 { 157 i, err := strconv.Atoi(vParts[1]) 158 if err != nil { 159 return Version{}, errors.Annotate(err, "Invalid version string, minor is not an int") 160 } 161 version.Minor = i 162 } 163 if len(vParts) == 3 { 164 version.Patch = vParts[2] 165 } 166 167 if version.Major == 2 && version.StorageEngine == WiredTiger { 168 return Version{}, errors.Errorf("Version 2.x does not support Wired Tiger storage engine") 169 } 170 171 // This deserialises the special "Mongo Upgrading" version 172 if version.Major == 0 && version.Minor == 0 { 173 return Version{StorageEngine: Upgrading}, nil 174 } 175 176 return version, nil 177 } 178 179 // String serializes the version into a string. 180 func (v Version) String() string { 181 s := fmt.Sprintf("%d.%d", v.Major, v.Minor) 182 if v.Patch != "" { 183 s = fmt.Sprintf("%s.%s", s, v.Patch) 184 } 185 if v.StorageEngine != "" { 186 s = fmt.Sprintf("%s/%s", s, v.StorageEngine) 187 } 188 return s 189 } 190 191 // JujuMongodPath returns the path for the mongod binary 192 // with the specified version. 193 func JujuMongodPath(v Version) string { 194 return fmt.Sprintf("/usr/lib/juju/mongo%d.%d/bin/mongod", v.Major, v.Minor) 195 } 196 197 var ( 198 // Mongo24 represents juju-mongodb 2.4.x 199 Mongo24 = Version{Major: 2, 200 Minor: 4, 201 Patch: "", 202 StorageEngine: MMAPV1, 203 } 204 // Mongo26 represents juju-mongodb26 2.6.x 205 Mongo26 = Version{Major: 2, 206 Minor: 6, 207 Patch: "", 208 StorageEngine: MMAPV1, 209 } 210 // Mongo32wt represents juju-mongodb3 3.2.x with wiredTiger storage. 211 Mongo32wt = Version{Major: 3, 212 Minor: 2, 213 Patch: "", 214 StorageEngine: WiredTiger, 215 } 216 // Mongo34wt represents 'mongodb-server-core' at version 3.4.x with WiredTiger 217 Mongo34wt = Version{Major: 3, 218 Minor: 4, 219 Patch: "", 220 StorageEngine: WiredTiger, 221 } 222 // Mongo36wt represents 'mongodb-server-core' at version 3.6.x with WiredTiger 223 Mongo36wt = Version{Major: 3, 224 Minor: 6, 225 Patch: "", 226 StorageEngine: WiredTiger, 227 } 228 // MongoUpgrade represents a sepacial case where an upgrade is in 229 // progress. 230 MongoUpgrade = Version{Major: 0, 231 Minor: 0, 232 Patch: "Upgrading", 233 StorageEngine: Upgrading, 234 } 235 ) 236 237 // WithAddresses represents an entity that has a set of 238 // addresses. e.g. a state Machine object 239 type WithAddresses interface { 240 Addresses() []network.Address 241 } 242 243 // IsMaster returns a boolean that represents whether the given 244 // machine's peer address is the primary mongo host for the replicaset 245 func IsMaster(session *mgo.Session, obj WithAddresses) (bool, error) { 246 addrs := obj.Addresses() 247 248 masterHostPort, err := replicaset.MasterHostPort(session) 249 250 // If the replica set has not been configured, then we 251 // can have only one master and the caller must 252 // be that master. 253 if err == replicaset.ErrMasterNotConfigured { 254 return true, nil 255 } 256 if err != nil { 257 return false, err 258 } 259 260 masterAddr, _, err := net.SplitHostPort(masterHostPort) 261 if err != nil { 262 return false, err 263 } 264 265 for _, addr := range addrs { 266 if addr.Value == masterAddr { 267 return true, nil 268 } 269 } 270 return false, nil 271 } 272 273 // SelectPeerAddress returns the address to use as the mongo replica set peer 274 // address by selecting it from the given addresses. If no addresses are 275 // available an empty string is returned. 276 func SelectPeerAddress(addrs []network.Address) string { 277 // ScopeMachineLocal addresses are never suitable for mongo peers, 278 // as each controller runs on a separate machine. 279 const allowMachineLocal = false 280 281 // The second bool result is ignored intentionally (we return an empty 282 // string if no suitable address is available.) 283 addr, _ := network.SelectControllerAddress(addrs, allowMachineLocal) 284 return addr.Value 285 } 286 287 // GenerateSharedSecret generates a pseudo-random shared secret (keyfile) 288 // for use with Mongo replica sets. 289 func GenerateSharedSecret() (string, error) { 290 // "A key’s length must be between 6 and 1024 characters and may 291 // only contain characters in the base64 set." 292 // -- http://docs.mongodb.org/manual/tutorial/generate-key-file/ 293 buf := make([]byte, base64.StdEncoding.DecodedLen(1024)) 294 if _, err := rand.Read(buf); err != nil { 295 return "", fmt.Errorf("cannot read random secret: %v", err) 296 } 297 return base64.StdEncoding.EncodeToString(buf), nil 298 } 299 300 // Path returns the executable path to be used to run mongod on this 301 // machine. If the juju-bundled version of mongo exists, it will return that 302 // path, otherwise it will return the command to run mongod from the path. 303 func Path(version Version) (string, error) { 304 return mongoPath(version, os.Stat, exec.LookPath) 305 } 306 307 func mongoPath( 308 version Version, 309 stat func(string) (os.FileInfo, error), 310 lookPath func(string) (string, error), 311 ) (string, error) { 312 // we don't want to match on patch so we remove it. 313 if version.Major == 2 && version.Minor == 4 { 314 if _, err := stat(JujuMongod24Path); err == nil { 315 return JujuMongod24Path, nil 316 } 317 path, err := lookPath("mongod") 318 if err != nil { 319 logger.Infof("could not find %v or mongod in $PATH", JujuMongod24Path) 320 return "", err 321 } 322 return path, nil 323 } 324 if version.Major == 3 && version.Minor == 6 { 325 if _, err := stat(MongodSystemPath); err == nil { 326 return MongodSystemPath, nil 327 } else { 328 return "", err 329 } 330 } 331 path := JujuMongodPath(version) 332 var err error 333 if _, err = stat(path); err == nil { 334 return path, nil 335 } 336 logger.Infof("could not find a suitable binary for %q", version) 337 errMsg := fmt.Sprintf("no suitable binary for %q", version) 338 return "", errors.New(errMsg) 339 } 340 341 /* 342 Values set as per bug: 343 https://bugs.launchpad.net/juju/+bug/1656430 344 net.ipv4.tcp_max_syn_backlog = 4096 345 net.core.somaxconn = 16384 346 net.core.netdev_max_backlog = 1000 347 net.ipv4.tcp_fin_timeout = 30 348 349 Values set as per mongod recommendation (see syslog on default mongod run) 350 /sys/kernel/mm/transparent_hugepage/enabled 'always' > 'never' 351 /sys/kernel/mm/transparent_hugepage/defrag 'always' > 'never' 352 */ 353 var mongoKernelTweaks = map[string]string{ 354 "/sys/kernel/mm/transparent_hugepage/enabled": "never", 355 "/sys/kernel/mm/transparent_hugepage/defrag": "never", 356 "/proc/sys/net/ipv4/tcp_max_syn_backlog": "4096", 357 "/proc/sys/net/core/somaxconn": "16384", 358 "/proc/sys/net/core/netdev_max_backlog": "1000", 359 "/proc/sys/net/ipv4/tcp_fin_timeout": "30", 360 } 361 362 // NewMemoryProfile returns a Memory Profile from the passed value. 363 func NewMemoryProfile(m string) (MemoryProfile, error) { 364 mp := MemoryProfile(m) 365 if err := mp.Validate(); err != nil { 366 return MemoryProfile(""), err 367 } 368 return mp, nil 369 } 370 371 // MemoryProfile represents a type of meory configuration for Mongo. 372 type MemoryProfile string 373 374 // String returns a string representation of this profile value. 375 func (m MemoryProfile) String() string { 376 return string(m) 377 } 378 379 func (m MemoryProfile) Validate() error { 380 if m != MemoryProfileLow && m != MemoryProfileDefault { 381 return errors.NotValidf("memory profile %q", m) 382 } 383 return nil 384 } 385 386 const ( 387 // MemoryProfileLow will use as little memory as possible in mongo. 388 MemoryProfileLow MemoryProfile = "low" 389 // MemoryProfileDefault will use mongo config ootb. 390 MemoryProfileDefault = "default" 391 ) 392 393 // EnsureServerParams is a parameter struct for EnsureServer. 394 type EnsureServerParams struct { 395 // APIPort is the port to connect to the api server. 396 APIPort int 397 398 // StatePort is the port to connect to the mongo server. 399 StatePort int 400 401 // Cert is the certificate. 402 Cert string 403 404 // PrivateKey is the certificate's private key. 405 PrivateKey string 406 407 // CAPrivateKey is the CA certificate's private key. 408 CAPrivateKey string 409 410 // SharedSecret is a secret shared between mongo servers. 411 SharedSecret string 412 413 // SystemIdentity is the identity of the system. 414 SystemIdentity string 415 416 // DataDir is the machine agent data directory. 417 DataDir string 418 419 // Namespace is the machine agent's namespace, which is used to 420 // generate a unique service name for Mongo. 421 Namespace string 422 423 // OplogSize is the size of the Mongo oplog. 424 // If this is zero, then EnsureServer will 425 // calculate a default size according to the 426 // algorithm defined in Mongo. 427 OplogSize int 428 429 // SetNUMAControlPolicy preference - whether the user 430 // wants to set the numa control policy when starting mongo. 431 SetNUMAControlPolicy bool 432 433 // MemoryProfile determines which value is going to be used by 434 // the cache and future memory tweaks. 435 MemoryProfile MemoryProfile 436 } 437 438 // EnsureServer ensures that the MongoDB server is installed, 439 // configured, and ready to run. 440 // 441 // This method will remove old versions of the mongo init service as necessary 442 // before installing the new version. 443 func EnsureServer(args EnsureServerParams) (Version, error) { 444 return ensureServer(args, mongoKernelTweaks) 445 } 446 447 func setupDataDirectory(args EnsureServerParams) error { 448 dbDir := DbDir(args.DataDir) 449 if err := os.MkdirAll(dbDir, 0700); err != nil { 450 return errors.Annotate(err, "cannot create mongo database directory") 451 } 452 453 if err := UpdateSSLKey(args.DataDir, args.Cert, args.PrivateKey); err != nil { 454 return errors.Trace(err) 455 } 456 457 err := utils.AtomicWriteFile(sharedSecretPath(args.DataDir), []byte(args.SharedSecret), 0600) 458 if err != nil { 459 return errors.Annotatef(err, "cannot write mongod shared secret to %v", sharedSecretPath(args.DataDir)) 460 } 461 462 if err := os.MkdirAll(logPath(dbDir), 0644); err != nil { 463 return errors.Annotate(err, "cannot create mongodb logging directory") 464 } 465 466 return nil 467 } 468 469 func ensureServer(args EnsureServerParams, mongoKernelTweaks map[string]string) (Version, error) { 470 var zeroVersion Version 471 tweakSysctlForMongo(mongoKernelTweaks) 472 473 if featureflag.Enabled(feature.MongoDbSnap) { 474 // TODO(tsm): push this to earlier in the bootstrapping process 475 if args.DataDir != dataPathForJujuDbSnap { 476 logger.Warningf("overwriting args.dataDir (set to %v) to %v", args.DataDir, dataPathForJujuDbSnap) 477 args.DataDir = dataPathForJujuDbSnap 478 } 479 } 480 481 logger.Infof( 482 "Ensuring mongo server is running; data directory %s; port %d", 483 args.DataDir, args.StatePort, 484 ) 485 486 setupDataDirectory(args) 487 488 if err := installMongod(series.MustHostSeries(), args.SetNUMAControlPolicy); err != nil { 489 // This isn't treated as fatal because the Juju MongoDB 490 // package is likely to be already installed anyway. There 491 // could just be a temporary issue with apt-get/yum/whatever 492 // and we don't want this to stop jujud from starting. 493 // (LP #1441904) 494 logger.Errorf("cannot install/upgrade mongod (will proceed anyway): %v", err) 495 } 496 finder := NewMongodFinder() 497 mongoPath, mongodVersion, err := finder.FindBest() 498 if err != nil { 499 return zeroVersion, errors.Trace(err) 500 } 501 logVersion(mongoPath) 502 503 oplogSizeMB := args.OplogSize 504 if oplogSizeMB == 0 { 505 oplogSizeMB, err = defaultOplogSize(DbDir(args.DataDir)) 506 if err != nil { 507 return zeroVersion, errors.Trace(err) 508 } 509 } 510 511 // Disable the default mongodb installed by the mongodb-server package. 512 // Only do this if the file doesn't exist already, so users can run 513 // their own mongodb server if they wish to. 514 if _, err := os.Stat(mongoConfigPath); os.IsNotExist(err) { 515 err = utils.AtomicWriteFile( 516 mongoConfigPath, 517 []byte("ENABLE_MONGODB=no"), 518 0644, 519 ) 520 if err != nil { 521 return zeroVersion, errors.Trace(err) 522 } 523 } 524 525 mongoArgs := generateConfig(mongoPath, args.DataDir, args.StatePort, oplogSizeMB, args.SetNUMAControlPolicy, mongodVersion, args.MemoryProfile) 526 logger.Debugf("creating mongo service configuration for mongo version: %d.%d.%s at %q", 527 mongoArgs.Version.Major, mongoArgs.Version.Minor, mongoArgs.Version.Patch, mongoArgs.MongoPath) 528 529 svc, err := mongoArgs.asService() 530 if err != nil { 531 return zeroVersion, errors.Trace(err) 532 } 533 534 // TODO(tsm): refactor out to service.Configure 535 if featureflag.Enabled(feature.MongoDbSnap) { 536 err = mongoArgs.writeConfig(configPath(args.DataDir)) 537 if err != nil { 538 return zeroVersion, errors.Trace(err) 539 } 540 541 err := snap.SetSnapConfig(ServiceName, "configpath", configPath(args.DataDir)) 542 if err != nil { 543 return zeroVersion, errors.Trace(err) 544 } 545 546 err = service.ManuallyRestart(svc) 547 if err != nil { 548 logger.Criticalf("unable to (re)start mongod service: %v", err) 549 return zeroVersion, errors.Trace(err) 550 } 551 552 return mongodVersion, nil 553 } 554 555 running, err := svc.Running() 556 if err != nil { 557 return zeroVersion, errors.Trace(err) 558 } 559 if running { 560 return mongodVersion, nil 561 } 562 563 dbDir := DbDir(args.DataDir) 564 if err := makeJournalDirs(dbDir); err != nil { 565 return zeroVersion, errors.Errorf("error creating journal directories: %v", err) 566 } 567 if err := preallocOplog(dbDir, oplogSizeMB); err != nil { 568 return zeroVersion, errors.Errorf("error creating oplog files: %v", err) 569 } 570 571 if err := service.InstallAndStart(svc); err != nil { 572 return zeroVersion, errors.Trace(err) 573 } 574 return mongodVersion, nil 575 } 576 577 func truncateAndWriteIfExists(procFile, value string) error { 578 if _, err := os.Stat(procFile); os.IsNotExist(err) { 579 logger.Debugf("%q does not exist, will not set %q", procFile, value) 580 return errors.Errorf("%q does not exist, will not set %q", procFile, value) 581 } 582 f, err := os.OpenFile(procFile, os.O_WRONLY|os.O_TRUNC, 0600) 583 if err != nil { 584 return errors.Trace(err) 585 } 586 defer f.Close() 587 _, err = f.WriteString(value) 588 return errors.Trace(err) 589 } 590 591 func tweakSysctlForMongo(editables map[string]string) { 592 for editableFile, value := range editables { 593 if err := truncateAndWriteIfExists(editableFile, value); err != nil { 594 logger.Errorf("could not set the value of %q to %q because of: %v\n", editableFile, value, err) 595 } 596 } 597 } 598 599 // UpdateSSLKey writes a new SSL key used by mongo to validate connections from Juju controller(s) 600 func UpdateSSLKey(dataDir, cert, privateKey string) error { 601 certKey := cert + "\n" + privateKey 602 err := utils.AtomicWriteFile(sslKeyPath(dataDir), []byte(certKey), 0600) 603 return errors.Annotate(err, "cannot write SSL key") 604 } 605 606 func makeJournalDirs(dataDir string) error { 607 journalDir := path.Join(dataDir, "journal") 608 if err := os.MkdirAll(journalDir, 0700); err != nil { 609 logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err) 610 return err 611 } 612 613 // Manually create the prealloc files, since otherwise they get 614 // created as 100M files. We create three files of 1MB each. 615 prefix := filepath.Join(journalDir, "prealloc.") 616 preallocSize := 1024 * 1024 617 return preallocFiles(prefix, preallocSize, preallocSize, preallocSize) 618 } 619 620 func logVersion(mongoPath string) { 621 cmd := exec.Command(mongoPath, "--version") 622 output, err := cmd.CombinedOutput() 623 if err != nil { 624 logger.Infof("failed to read the output from %s --version: %v", mongoPath, err) 625 return 626 } 627 logger.Debugf("using mongod: %s --version: %q", mongoPath, output) 628 } 629 630 func installPackage(pkg string, pacconfer config.PackagingConfigurer, pacman manager.PackageManager) error { 631 // apply release targeting if needed. 632 if pacconfer.IsCloudArchivePackage(pkg) { 633 pkg = strings.Join(pacconfer.ApplyCloudArchiveTarget(pkg), " ") 634 } 635 636 return pacman.Install(pkg) 637 } 638 639 func installMongod(operatingsystem string, numaCtl bool) error { 640 if featureflag.Enabled(feature.MongoDbSnap) { 641 prerequisites := []snap.App{snap.NewApp("core")} 642 backgroundServices := []snap.BackgroundService{{"daemon", true}} 643 conf := common.Conf{Desc: ServiceName + " snap"} 644 service, err := snap.NewService(ServiceName, conf, snap.Command, "edge", "jailmode", backgroundServices, prerequisites) 645 if err != nil { 646 return errors.Trace(err) 647 } 648 return service.Install() 649 } 650 651 // fetch the packaging configuration manager for the current operating system. 652 pacconfer, err := config.NewPackagingConfigurer(operatingsystem) 653 if err != nil { 654 return err 655 } 656 657 // fetch the package manager implementation for the current operating system. 658 pacman, err := manager.NewPackageManager(operatingsystem) 659 if err != nil { 660 return err 661 } 662 663 // CentOS requires "epel-release" for the epel repo mongodb-server is in. 664 if operatingsystem == "centos7" { 665 // install epel-release 666 if err := pacman.Install("epel-release"); err != nil { 667 return err 668 } 669 } 670 mongoPkgs, fallbackPkgs := packagesForSeries(operatingsystem) 671 672 if numaCtl { 673 logger.Infof("installing %v and %s", mongoPkgs, numaCtlPkg) 674 if err = installPackage(numaCtlPkg, pacconfer, pacman); err != nil { 675 return errors.Trace(err) 676 } 677 } else { 678 logger.Infof("installing %v", mongoPkgs) 679 } 680 681 for i := range mongoPkgs { 682 if err = installPackage(mongoPkgs[i], pacconfer, pacman); err != nil { 683 break 684 } 685 } 686 if err != nil && len(fallbackPkgs) == 0 { 687 return errors.Trace(err) 688 } 689 if err != nil { 690 logger.Errorf("installing mongo failed: %v", err) 691 logger.Infof("will try fallback packages %v", fallbackPkgs) 692 for i := range fallbackPkgs { 693 if err = installPackage(fallbackPkgs[i], pacconfer, pacman); err != nil { 694 return errors.Trace(err) 695 } 696 } 697 } 698 699 // Work around SELinux on centos7 700 if operatingsystem == "centos7" { 701 cmd := []string{"chcon", "-R", "-v", "-t", "mongod_var_lib_t", "/var/lib/juju/"} 702 logger.Infof("running %s %v", cmd[0], cmd[1:]) 703 _, err = utils.RunCommand(cmd[0], cmd[1:]...) 704 if err != nil { 705 logger.Errorf("chcon failed to change file security context error %s", err) 706 return err 707 } 708 709 cmd = []string{"semanage", "port", "-a", "-t", "mongod_port_t", "-p", "tcp", strconv.Itoa(controller.DefaultStatePort)} 710 logger.Infof("running %s %v", cmd[0], cmd[1:]) 711 _, err = utils.RunCommand(cmd[0], cmd[1:]...) 712 if err != nil { 713 if !strings.Contains(err.Error(), "exit status 1") { 714 logger.Errorf("semanage failed to provide access on port %d error %s", controller.DefaultStatePort, err) 715 return err 716 } 717 } 718 } 719 return nil 720 } 721 722 // packagesForSeries returns the name of the mongo package for the series 723 // of the machine that it is going to be running on plus a fallback for 724 // options where the package is going to be ready eventually but might not 725 // yet be. 726 func packagesForSeries(series string) ([]string, []string) { 727 switch series { 728 case "precise", "centos7": 729 return []string{"mongodb-server"}, []string{} 730 case "trusty": 731 return []string{"juju-mongodb"}, []string{} 732 case "xenial", "artful": 733 return []string{JujuMongoPackage, JujuMongoToolsPackage}, []string{} 734 default: 735 // Bionic and newer 736 return []string{"mongodb-server-core", "mongodb-clients"}, []string{} 737 } 738 } 739 740 // DbDir returns the dir where mongo storage is. 741 func DbDir(dataDir string) string { 742 return filepath.Join(dataDir, "db") 743 }