github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/peergrouper/initiate.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package peergrouper 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/replicaset" 11 "github.com/juju/utils" 12 "gopkg.in/mgo.v2" 13 14 "github.com/juju/juju/agent" 15 "github.com/juju/juju/mongo" 16 ) 17 18 var initiateAttemptStrategy = utils.AttemptStrategy{ 19 Total: 60 * time.Second, 20 Delay: 5 * time.Second, 21 } 22 23 // InitiateMongoParams holds parameters for the MaybeInitiateMongo call. 24 type InitiateMongoParams struct { 25 // DialInfo specifies how to connect to the mongo server. 26 DialInfo *mgo.DialInfo 27 28 // MemberHostPort provides the address to use for 29 // the first replica set member. 30 MemberHostPort string 31 32 // User holds the user to log as in to the mongo server. 33 // If it is empty, no login will take place. 34 User string 35 Password string 36 } 37 38 // MaybeInitiateMongoServer is a convenience function for initiating a mongo 39 // replicaset only if it is not already initiated. 40 func MaybeInitiateMongoServer(p InitiateMongoParams) error { 41 return InitiateMongoServer(p, false) 42 } 43 44 // InitiateMongoServer checks for an existing mongo configuration. 45 // If no existing configuration is found one is created using Initiate. 46 // If force flag is true, the configuration will be started anyway. 47 func InitiateMongoServer(p InitiateMongoParams, force bool) error { 48 logger.Debugf("Initiating mongo replicaset; dialInfo %#v; memberHostport %q; user %q; password %q", p.DialInfo, p.MemberHostPort, p.User, p.Password) 49 defer logger.Infof("finished MaybeInitiateMongoServer") 50 51 if len(p.DialInfo.Addrs) > 1 { 52 logger.Infof("more than one member; replica set must be already initiated") 53 return nil 54 } 55 p.DialInfo.Direct = true 56 57 // TODO(rog) remove this code when we no longer need to upgrade 58 // from pre-HA-capable environments. 59 if p.User != "" { 60 p.DialInfo.Username = p.User 61 p.DialInfo.Password = p.Password 62 } 63 64 // Initiate may fail while mongo is initialising, so we retry until 65 // we successfully populate the replicaset config. 66 var err error 67 for attempt := initiateAttemptStrategy.Start(); attempt.Next(); { 68 err = attemptInitiateMongoServer(p.DialInfo, p.MemberHostPort, force) 69 if err == nil || err == ErrReplicaSetAlreadyInitiated { 70 logger.Infof("replica set initiated") 71 return err 72 } 73 if attempt.HasNext() { 74 logger.Debugf("replica set initiation failed, will retry: %v", err) 75 } 76 } 77 return errors.Annotatef(err, "cannot initiate replica set") 78 } 79 80 // ErrReplicaSetAlreadyInitiated is returned with attemptInitiateMongoServer is called 81 // on a mongo with an existing relicaset, it helps to assert which of the two valid 82 // paths was taking when calling MaybeInitiateMongoServer/InitiateMongoServer which 83 // is useful for testing purposes and also when debugging cases of faulty replica sets 84 var ErrReplicaSetAlreadyInitiated = errors.New("replicaset is already initiated") 85 86 // attemptInitiateMongoServer attempts to initiate the replica set. 87 func attemptInitiateMongoServer(dialInfo *mgo.DialInfo, memberHostPort string, force bool) error { 88 session, err := mgo.DialWithInfo(dialInfo) 89 if err != nil { 90 return errors.Annotatef(err, "cannot dial mongo to initiate replicaset") 91 } 92 defer session.Close() 93 session.SetSocketTimeout(mongo.SocketTimeout) 94 cfg, err := replicaset.CurrentConfig(session) 95 if err != nil && err != mgo.ErrNotFound { 96 return errors.Errorf("cannot get replica set configuration: %v", err) 97 } 98 if !force && err == nil && len(cfg.Members) > 0 { 99 logger.Infof("replica set configuration found: %#v", cfg) 100 return ErrReplicaSetAlreadyInitiated 101 } 102 103 return replicaset.Initiate( 104 session, 105 memberHostPort, 106 mongo.ReplicaSetName, 107 map[string]string{ 108 jujuMachineKey: agent.BootstrapMachineId, 109 }, 110 ) 111 }