github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/agent/mongo/mongo.go (about) 1 package mongo 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "path" 8 "path/filepath" 9 10 "github.com/juju/loggo" 11 12 "launchpad.net/juju-core/upstart" 13 "launchpad.net/juju-core/utils" 14 ) 15 16 const ( 17 maxFiles = 65000 18 maxProcs = 20000 19 ) 20 21 var ( 22 logger = loggo.GetLogger("juju.agent.mongo") 23 24 oldMongoServiceName = "juju-db" 25 26 // JujuMongodPath holds the default path to the juju-specific mongod. 27 JujuMongodPath = "/usr/lib/juju/bin/mongod" 28 ) 29 30 // MongoPath returns the executable path to be used to run mongod on this 31 // machine. If the juju-bundled version of mongo exists, it will return that 32 // path, otherwise it will return the command to run mongod from the path. 33 func MongodPath() (string, error) { 34 if _, err := os.Stat(JujuMongodPath); err == nil { 35 return JujuMongodPath, nil 36 } 37 38 path, err := exec.LookPath("mongod") 39 if err != nil { 40 return "", err 41 } 42 return path, nil 43 } 44 45 // EnsureMongoServer ensures that the correct mongo upstart script is installed 46 // and running. 47 // 48 // This method will remove old versions of the mongo upstart script as necessary 49 // before installing the new version. 50 func EnsureMongoServer(dir string, port int) error { 51 name := makeServiceName(mongoScriptVersion) 52 service, err := MongoUpstartService(name, dir, port) 53 if err != nil { 54 return err 55 } 56 if service.Installed() { 57 return nil 58 } 59 60 if err := removeOldMongoServices(mongoScriptVersion); err != nil { 61 return err 62 } 63 64 if err := makeJournalDirs(dir); err != nil { 65 return err 66 } 67 68 if err := service.Install(); err != nil { 69 return fmt.Errorf("failed to install mongo service %q: %v", service.Name, err) 70 } 71 return service.Start() 72 } 73 74 func makeJournalDirs(dir string) error { 75 journalDir := path.Join(dir, "journal") 76 77 if err := os.MkdirAll(journalDir, 0700); err != nil { 78 logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err) 79 return err 80 } 81 82 // manually create the prealloc files, since otherwise they get created as 100M files. 83 zeroes := make([]byte, 64*1024) // should be enough for anyone 84 for x := 0; x < 3; x++ { 85 name := fmt.Sprintf("prealloc.%d", x) 86 filename := filepath.Join(journalDir, name) 87 f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700) 88 if err != nil { 89 return fmt.Errorf("failed to open mongo prealloc file %q: %v", filename, err) 90 } 91 defer f.Close() 92 for total := 0; total < 1024*1024; { 93 n, err := f.Write(zeroes) 94 if err != nil { 95 return fmt.Errorf("failed to write to mongo prealloc file %q: %v", filename, err) 96 } 97 total += n 98 } 99 } 100 return nil 101 } 102 103 // removeOldMongoServices looks for any old juju mongo upstart scripts and 104 // removes them. 105 func removeOldMongoServices(curVersion int) error { 106 old := upstart.NewService(oldMongoServiceName) 107 if err := old.StopAndRemove(); err != nil { 108 logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err) 109 return err 110 } 111 112 // the new formatting for the script name started at version 2 113 for x := 2; x < curVersion; x++ { 114 old := upstart.NewService(makeServiceName(x)) 115 if err := old.StopAndRemove(); err != nil { 116 logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err) 117 return err 118 } 119 } 120 return nil 121 } 122 123 func makeServiceName(version int) string { 124 return fmt.Sprintf("juju-db-v%d", version) 125 } 126 127 // mongoScriptVersion keeps track of changes to the mongo upstart script. 128 // Update this version when you update the script that gets installed from 129 // MongoUpstartService. 130 const mongoScriptVersion = 2 131 132 // MongoUpstartService returns the upstart config for the mongo state service. 133 // 134 // This method assumes there is a server.pem keyfile in dataDir. 135 func MongoUpstartService(name, dataDir string, port int) (*upstart.Conf, error) { 136 137 keyFile := path.Join(dataDir, "server.pem") 138 svc := upstart.NewService(name) 139 140 dbDir := path.Join(dataDir, "db") 141 142 conf := &upstart.Conf{ 143 Service: *svc, 144 Desc: "juju state database", 145 Limit: map[string]string{ 146 "nofile": fmt.Sprintf("%d %d", maxFiles, maxFiles), 147 "nproc": fmt.Sprintf("%d %d", maxProcs, maxProcs), 148 }, 149 Cmd: "/usr/bin/mongod" + 150 " --auth" + 151 " --dbpath=" + dbDir + 152 " --sslOnNormalPorts" + 153 " --sslPEMKeyFile " + utils.ShQuote(keyFile) + 154 " --sslPEMKeyPassword ignored" + 155 " --bind_ip 0.0.0.0" + 156 " --port " + fmt.Sprint(port) + 157 " --noprealloc" + 158 " --syslog" + 159 " --smallfiles", 160 // TODO(Nate): uncomment when we commit HA stuff 161 // + 162 // " --replSet juju", 163 } 164 return conf, nil 165 }