github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/agent/mongo/upgrade.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 "fmt" 8 "os" 9 "os/exec" 10 "syscall" 11 "time" 12 13 "labix.org/v2/mgo" 14 15 "github.com/juju/juju/upstart" 16 ) 17 18 const mongoSocketTimeout = 10 * time.Second 19 20 var ( 21 processSignal = (*os.Process).Signal 22 ) 23 24 type EnsureAdminUserParams struct { 25 // DialInfo specifies how to connect to the mongo server. 26 DialInfo *mgo.DialInfo 27 // Namespace is the agent namespace, used to derive the Mongo service name. 28 Namespace string 29 // DataDir is the Juju data directory, used to start a --noauth server. 30 DataDir string 31 // Port is the listening port of the Mongo server. 32 Port int 33 // User holds the user to log in to the mongo server as. 34 User string 35 // Password holds the password for the user to log in as. 36 Password string 37 } 38 39 // EnsureAdminUser ensures that the specified user and password 40 // are added to the admin database. 41 // 42 // This function will stop the Mongo service if it needs to add 43 // the admin user, as it must restart Mongo in --noauth mode. 44 func EnsureAdminUser(p EnsureAdminUserParams) (added bool, err error) { 45 if len(p.DialInfo.Addrs) > 1 { 46 logger.Infof("more than one state server; admin user must exist") 47 return false, nil 48 } 49 p.DialInfo.Addrs = []string{fmt.Sprintf("127.0.0.1:%d", p.Port)} 50 p.DialInfo.Direct = true 51 52 // Attempt to login to the admin database first. 53 session, err := mgo.DialWithInfo(p.DialInfo) 54 if err != nil { 55 return false, fmt.Errorf("can't dial mongo to ensure admin user: %v", err) 56 } 57 session.SetSocketTimeout(mongoSocketTimeout) 58 err = session.DB("admin").Login(p.User, p.Password) 59 session.Close() 60 if err == nil { 61 return false, nil 62 } 63 logger.Debugf("admin login failed: %v", err) 64 65 // Login failed, so we need to add the user. 66 // Stop mongo, so we can start it in --noauth mode. 67 mongoServiceName := ServiceName(p.Namespace) 68 mongoService := upstart.NewService(mongoServiceName) 69 if err := upstartServiceStop(mongoService); err != nil { 70 return false, fmt.Errorf("failed to stop %v: %v", mongoServiceName, err) 71 } 72 73 // Start mongod in --noauth mode. 74 logger.Debugf("starting mongo with --noauth") 75 cmd, err := noauthCommand(p.DataDir, p.Port) 76 if err != nil { 77 return false, fmt.Errorf("failed to prepare mongod command: %v", err) 78 } 79 if err := cmd.Start(); err != nil { 80 return false, fmt.Errorf("failed to start mongod: %v", err) 81 } 82 defer cmd.Process.Kill() 83 84 // Add the user to the admin database. 85 logger.Debugf("setting admin password") 86 if session, err = mgo.DialWithInfo(p.DialInfo); err != nil { 87 return false, fmt.Errorf("can't dial mongo to ensure admin user: %v", err) 88 } 89 err = session.DB("admin").AddUser(p.User, p.Password, false) 90 session.Close() 91 if err != nil { 92 return false, fmt.Errorf("failed to add %q to admin database: %v", p.User, err) 93 } 94 logger.Infof("added %q to admin database", p.User) 95 96 // Restart mongo using upstart. 97 if err := processSignal(cmd.Process, syscall.SIGTERM); err != nil { 98 return false, fmt.Errorf("cannot kill mongod: %v", err) 99 } 100 if err := cmd.Wait(); err != nil { 101 if _, ok := err.(*exec.ExitError); !ok { 102 return false, fmt.Errorf("mongod did not cleanly terminate: %v", err) 103 } 104 } 105 if err := upstartServiceStart(mongoService); err != nil { 106 return false, err 107 } 108 return true, nil 109 }