github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/mongo/open.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  	"crypto/tls"
     8  	"crypto/x509"
     9  	stderrors "errors"
    10  	"fmt"
    11  	"net"
    12  	"time"
    13  
    14  	"labix.org/v2/mgo"
    15  
    16  	"github.com/juju/juju/cert"
    17  )
    18  
    19  // SocketTimeout should be long enough that
    20  // even a slow mongo server will respond in that
    21  // length of time. Since mongo servers ping themselves
    22  // every 10 seconds, we use a value of just over 2
    23  // ping periods to allow for delayed pings due to
    24  // issues such as CPU starvation etc.
    25  const SocketTimeout = 21 * time.Second
    26  
    27  // defaultDialTimeout should be representative of
    28  // the upper bound of time taken to dial a mongo
    29  // server from within the same cloud/private network.
    30  const defaultDialTimeout = 30 * time.Second
    31  
    32  // DialOpts holds configuration parameters that control the
    33  // Dialing behavior when connecting to a state server.
    34  type DialOpts struct {
    35  	// Timeout is the amount of time to wait contacting
    36  	// a state server.
    37  	Timeout time.Duration
    38  }
    39  
    40  // DefaultDialOpts returns a DialOpts representing the default
    41  // parameters for contacting a state server.
    42  func DefaultDialOpts() DialOpts {
    43  	return DialOpts{Timeout: defaultDialTimeout}
    44  }
    45  
    46  // Info encapsulates information about cluster of
    47  // mongo servers and can be used to make a
    48  // connection to that cluster.
    49  type Info struct {
    50  	// Addrs gives the addresses of the MongoDB servers for the state.
    51  	// Each address should be in the form address:port.
    52  	Addrs []string
    53  
    54  	// CACert holds the CA certificate that will be used
    55  	// to validate the state server's certificate, in PEM format.
    56  	CACert string
    57  }
    58  
    59  // DialInfo returns information on how to dial
    60  // the state's mongo server with the given info
    61  // and dial options.
    62  func DialInfo(info Info, opts DialOpts) (*mgo.DialInfo, error) {
    63  	if len(info.Addrs) == 0 {
    64  		return nil, stderrors.New("no mongo addresses")
    65  	}
    66  	if len(info.CACert) == 0 {
    67  		return nil, stderrors.New("missing CA certificate")
    68  	}
    69  	xcert, err := cert.ParseCert(info.CACert)
    70  	if err != nil {
    71  		return nil, fmt.Errorf("cannot parse CA certificate: %v", err)
    72  	}
    73  	pool := x509.NewCertPool()
    74  	pool.AddCert(xcert)
    75  	tlsConfig := &tls.Config{
    76  		RootCAs:    pool,
    77  		ServerName: "anything",
    78  	}
    79  	dial := func(addr net.Addr) (net.Conn, error) {
    80  		c, err := net.Dial("tcp", addr.String())
    81  		if err != nil {
    82  			logger.Debugf("connection failed, will retry: %v", err)
    83  			return nil, err
    84  		}
    85  		cc := tls.Client(c, tlsConfig)
    86  		if err := cc.Handshake(); err != nil {
    87  			logger.Errorf("TLS handshake failed: %v", err)
    88  			return nil, err
    89  		}
    90  		logger.Infof("dialled mongo successfully")
    91  		return cc, nil
    92  	}
    93  
    94  	return &mgo.DialInfo{
    95  		Addrs:   info.Addrs,
    96  		Timeout: opts.Timeout,
    97  		Dial:    dial,
    98  	}, nil
    99  }