github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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/utils" 11 "gopkg.in/mgo.v2" 12 13 "github.com/juju/juju/agent" 14 "github.com/juju/juju/mongo" 15 "github.com/juju/juju/replicaset" 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 succssfully 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 { 70 logger.Infof("replica set initiated") 71 return nil 72 } 73 if attempt.HasNext() { 74 logger.Debugf("replica set initiation failed, will retry: %v", err) 75 } 76 } 77 if err == ErrReplicaSetAlreadyInitiated { 78 return err 79 } 80 return errors.Annotatef(err, "cannot initiate replica set") 81 } 82 83 // ErrReplicaSetAlreadyInitiated is returned with attemptInitiateMongoServer is called 84 // on a mongo with an existing relicaset, it helps to assert which of the two valid 85 // paths was taking when calling MaybeInitiateMongoServer/InitiateMongoServer which 86 // is useful for testing purposes and also when debugging cases of faulty replica sets 87 var ErrReplicaSetAlreadyInitiated = errors.New("replicaset is already initiated") 88 89 // attemptInitiateMongoServer attempts to initiate the replica set. 90 func attemptInitiateMongoServer(dialInfo *mgo.DialInfo, memberHostPort string, force bool) error { 91 session, err := mgo.DialWithInfo(dialInfo) 92 if err != nil { 93 return errors.Annotatef(err, "cannot dial mongo to initiate replicaset") 94 } 95 defer session.Close() 96 session.SetSocketTimeout(mongo.SocketTimeout) 97 cfg, err := replicaset.CurrentConfig(session) 98 if err != nil && err != mgo.ErrNotFound { 99 return errors.Errorf("cannot get replica set configuration: %v", err) 100 } 101 if !force && err == nil && len(cfg.Members) > 0 { 102 logger.Infof("replica set configuration found: %#v", cfg) 103 return ErrReplicaSetAlreadyInitiated 104 } 105 106 return replicaset.Initiate( 107 session, 108 memberHostPort, 109 mongo.ReplicaSetName, 110 map[string]string{ 111 jujuMachineKey: agent.BootstrapMachineId, 112 }, 113 ) 114 }