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  }