github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/mongo/admin.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 "net" 9 "os" 10 "os/exec" 11 "strconv" 12 "syscall" 13 14 "github.com/juju/errors" 15 "gopkg.in/mgo.v2" 16 ) 17 18 // AdminUser is the name of the user that is initially created in mongo. 19 const AdminUser = "admin" 20 21 var ( 22 processSignal = (*os.Process).Signal 23 ) 24 25 type EnsureAdminUserParams struct { 26 // DialInfo specifies how to connect to the mongo server. 27 DialInfo *mgo.DialInfo 28 // Namespace is the agent namespace, used to derive the Mongo service name. 29 Namespace string 30 // DataDir is the Juju data directory, used to start a --noauth server. 31 DataDir string 32 // Port is the listening port of the Mongo server. 33 Port int 34 // User holds the user to log in to the mongo server as. 35 User string 36 // Password holds the password for the user to log in as. 37 Password string 38 } 39 40 // EnsureAdminUser ensures that the specified user and password 41 // are added to the admin database. 42 // 43 // This function will stop the Mongo service if it needs to add 44 // the admin user, as it must restart Mongo in --noauth mode. 45 func EnsureAdminUser(p EnsureAdminUserParams) (added bool, err error) { 46 portStr := strconv.Itoa(p.Port) 47 localIPv4Addr := net.JoinHostPort("127.0.0.1", portStr) 48 localIPv6Addr := net.JoinHostPort("::1", portStr) 49 if len(p.DialInfo.Addrs) > 1 { 50 // Verify the addresses are for different servers. 51 for _, addr := range p.DialInfo.Addrs { 52 switch addr { 53 case localIPv4Addr, localIPv6Addr: 54 continue 55 default: 56 logger.Infof("more than one state server; admin user must exist") 57 return false, nil 58 } 59 } 60 } 61 p.DialInfo.Addrs = []string{localIPv4Addr, localIPv6Addr} 62 p.DialInfo.Direct = true 63 64 // Attempt to login to the admin database first. 65 session, err := mgo.DialWithInfo(p.DialInfo) 66 if err != nil { 67 return false, fmt.Errorf("can't dial mongo to ensure admin user: %v", err) 68 } 69 session.SetSocketTimeout(SocketTimeout) 70 err = session.DB("admin").Login(p.User, p.Password) 71 session.Close() 72 if err == nil { 73 return false, nil 74 } 75 logger.Debugf("admin login failed: %v", err) 76 77 // Login failed, so we need to add the user. 78 // Stop mongo, so we can start it in --noauth mode. 79 mongoServiceName := ServiceName(p.Namespace) 80 mongoService, err := discoverService(mongoServiceName) 81 if err != nil { 82 return false, errors.Annotatef(err, "failed to discover service", mongoServiceName) 83 } 84 if err := mongoService.Stop(); err != nil { 85 return false, fmt.Errorf("failed to stop %v: %v", mongoServiceName, err) 86 } 87 88 // Start mongod in --noauth mode. 89 logger.Debugf("starting mongo with --noauth") 90 cmd, err := noauthCommand(p.DataDir, p.Port) 91 if err != nil { 92 return false, fmt.Errorf("failed to prepare mongod command: %v", err) 93 } 94 if err := cmd.Start(); err != nil { 95 return false, fmt.Errorf("failed to start mongod: %v", err) 96 } 97 defer cmd.Process.Kill() 98 99 // Add the user to the admin database. 100 logger.Debugf("setting admin password") 101 if session, err = mgo.DialWithInfo(p.DialInfo); err != nil { 102 return false, fmt.Errorf("can't dial mongo to ensure admin user: %v", err) 103 } 104 err = SetAdminMongoPassword(session, p.User, p.Password) 105 session.Close() 106 if err != nil { 107 return false, fmt.Errorf("failed to add %q to admin database: %v", p.User, err) 108 } 109 logger.Infof("added %q to admin database", p.User) 110 111 // Restart mongo using the init system. 112 if err := processSignal(cmd.Process, syscall.SIGTERM); err != nil { 113 return false, fmt.Errorf("cannot kill mongod: %v", err) 114 } 115 if err := cmd.Wait(); err != nil { 116 if _, ok := err.(*exec.ExitError); !ok { 117 return false, fmt.Errorf("mongod did not cleanly terminate: %v", err) 118 } 119 } 120 if err := mongoService.Start(); err != nil { 121 return false, err 122 } 123 return true, nil 124 } 125 126 // SetAdminMongoPassword sets the administrative password 127 // to access a mongo database. If the password is non-empty, 128 // all subsequent attempts to access the database must 129 // be authorized; otherwise no authorization is required. 130 func SetAdminMongoPassword(session *mgo.Session, user, password string) error { 131 admin := session.DB("admin") 132 if password != "" { 133 if err := admin.UpsertUser(&mgo.User{ 134 Username: user, 135 Password: password, 136 Roles: []mgo.Role{mgo.RoleDBAdminAny, mgo.RoleUserAdminAny, mgo.RoleClusterAdmin, mgo.RoleReadWriteAny}, 137 }); err != nil { 138 return fmt.Errorf("cannot set admin password: %v", err) 139 } 140 } else { 141 if err := admin.RemoveUser(user); err != nil && err != mgo.ErrNotFound { 142 return fmt.Errorf("cannot disable admin password: %v", err) 143 } 144 } 145 return nil 146 }