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 }