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  }