Go, MongoDB, and Compose

This document assumes an environmental variable called MONGODB_URL with your Compose Connection string. To set this variable, execute the following in the shell:

export MONGODB_URL="mongodb://user:[email protected]/db_name"

The mgo driver is the standard MongoDB driver for go, and can be installed by:

go get github.com/globalsign/mgo

The mgo documentation, which is available at https://godoc.org/github.com/globalsign/mgo, covers most typical connections but as Compose MongoDB has TLS/SSL enabled, there are some extra steps to create and pass the TLS configuration to the mgo Dial function.

Connecting with Go with TLS/SSL and certificate validation

When connecting to MongoDB, database connections over TLS/SSL are typically validated with a certificate from Lets Encrypt over the public key validation infrastructure. That, practically, means that you only need the connection string URL to connect. In our example below, we place that in the MONGODB_URL environment variable.

In some situations, MongoDB on Compose will be deployed using self signed certificates which are available through the user interface. Those certificates will need to be saved to a file and located by the application. In the example below, this is done by setting the path to the certificate in the MONGODB_CERT_PATH environment variable.

package main

import (
    "crypto/tls"
    "crypto/x509"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "os"
    "strings"

    "github.com/globalsign/mgo"
    "github.com/globalsign/mgo/bson"
)

var session *mgo.Session


func main() {
    connectionString, present := os.LookupEnv("MONGODB_URL")
    if !present {
        log.Fatal("Need to set MONGODB_URL environment variable")
    }

    certpath, present := os.LookupEnv("MONGODB_CERT_PATH")

    tlsConfig := &tls.Config{}

    if present {
        roots := x509.NewCertPool()
        if ca, err := ioutil.ReadFile(certpath); err == nil {
            roots.AppendCertsFromPEM(ca)
        }
        tlsConfig.RootCAs = roots
    }
  
    trimmedConnectionString := strings.TrimSuffix(connectionString, "?ssl=true")

  dialInfo, err := mgo.ParseURL(trimmedConnectionString)
    if err != nil {
        log.Fatal(err)
    }

    dialInfo.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
        return tls.Dial("tcp", addr.String(), tlsConfig)
    }

  session, err = mgo.DialWithInfo(dialInfo)
    if err != nil {
        log.Fatal(err)
    }

    defer session.Close()

    dbnames, err := session.DB("").CollectionNames()
    if err != nil {
        fmt.Println("Couldn't query for collections names: ", err)
        os.Exit(1)
    }

    fmt.Println(dbnames)

}

Note that the line that removes "?ssl=true" from the URI (19) is needed as mgo will currently error if given the standard parameter for SSL/TLS connections. Mgo is being enhanced so that it will use the ?ssl=true parameter, reducing this example to pretty much mgo.Dial(url), but as of writing, this update is not in general circulation.


Still Need Help?

If this article didn't solve things, summon a human and get some help!