Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Organizing Environment Variables Golang

In Node.js I use the nconf module to house environment variables like S3 keys, GCM keys, etc for each of my projects.

I haven't been able to find a similar solution in Go.

What are the generally accepted tools to help manage environment variables for each Go project?

Thanks in advance.

like image 663
Jorge Olivero Avatar asked Jul 21 '14 20:07

Jorge Olivero


People also ask

What is os Getenv in Golang?

Go os. The Getenv retrieves the value of the environment variable named by the key. It returns the value, which will be empty if the variable is not present. To distinguish between an empty value and an unset value, use LookupEnv . get_env.go.

What is $Git_dir?

GIT_DIR is the location of the . git folder. If this isn't specified, Git walks up the directory tree until it gets to ~ or / , looking for a . git directory at every step.


2 Answers

I would strongly recommend using github.com/namsral/flag instead. It's like the built in flag except you can also supply the parameters via environment variables.

For example, suppose you have this code:

package main

import "fmt"
import "github.com/namsral/flag"

func main() {
    var port = 3000
    flag.IntVar(&port, "port", port, "Port number")
    flag.Parse()
    fmt.Println("You seem to prefer", port)
}

Then you can supply the values with either a command line option or an environment variable:

:~/dev/GO$ go run dummy.go
You seem to prefer 3000
:~/dev/GO$ go run dummy.go -port=1234
You seem to prefer 1234
:~/dev/GO$ PORT=4321 go run dummy.go
You seem to prefer 4321
:~/dev/GO$ PORT=4321 go run dummy.go -port=5555
You seem to prefer 5555

This might matter when it's hard to supply command line args. For example, if you use gin to automatically restart a server you have no way to supply command line arguments since gin is just calling go run on the main code without any arguments passed along.

like image 180
Peter Bengtsson Avatar answered Oct 02 '22 13:10

Peter Bengtsson


I did some reading on this a while back when I was getting started with Go. According to this link, http://peter.bourgon.org/go-in-production/, they recommend using CLI flags (parameters) instead of environment vars - they even convert environment vars to flags to their CLI apps.

It took some getting used to; but, I really do see the advantages of going pure CLI flags between development, staging and production environments - having specific scripts for each environment.

For example, here's a little web app I wrote recently:

// global flags
var isdebug bool
var port int
var cert string
var key string
var dbdsn string
var dbmaxidle int
var dbmaxopen int
var imguri string

// init is the entry point for the entire web application.
func init() {

    log.Println("Starting wwwgo ...")

    // setup the flags
    //flag.StringVar(&host, "host", "", "Specify a host to redirect to. Use this to redirect all traffic to a single url.")
    flag.IntVar(&port, "port", 8080, "Specify the port to listen to.")
    flag.BoolVar(&isdebug, "isdebug", false, "Set to true to run the app in debug mode.  In debug, it may panic on some errors.")
    flag.StringVar(&cert, "cert", "", "Enables listening on 443 with -cert and -key files specified.  This must be a full path to the certificate .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.")
    flag.StringVar(&key, "key", "", "Enables listening on 443 with -cert and -key files specified.  This must be a full path to the key .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.")
    flag.StringVar(&dbdsn, "dbdsn", "root:root@tcp(localhost:3306)/dev_db?timeout=5s&tls=false&autocommit=true", "Specifies the MySql DSN connection.")
    flag.IntVar(&dbmaxidle, "dbmaxidle", 0, "Sets the database/sql MaxIdleConns.")
    flag.IntVar(&dbmaxopen, "dbmaxopen", 500, "Sets the database/sql MaxOpenConns.")
    flag.StringVar(&imguri, "imguri", "/cdn/uploads/", "Set this to the full base uri of all images, for example on a remote CDN server or local relative virtual directory.")
    flag.Parse()

    // log our flags
    if isdebug != false {
        log.Println("DEBUG mode enabled")
    }
    if cert != "" && key != "" {
        log.Println("Attempting SSL binding with supplied cert and key.")
    }
    if dbdsn != "" {
        log.Printf("Using default dbdsn: %s", dbdsn)
    }

    ...
}

This really becomes nice in staging/production environments.

A quick ./wwwgo -h for "what the heck was that parameter?" gives you full documentation:

admin@dev01:~/code/frontend/src/wwwgo [master]$ ./wwwgo -h
Usage of ./wwwgo:
  -cert="": Enables listening on 443 with -cert and -key files specified.  This must be a full path to the certificate .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.
  -dbdsn="root:root@tcp(localhost:3306)/dev_db?timeout=5s&tls=false&autocommit=true": Specifies the MySql DSN connection.
  -dbmaxidle=0: Sets the database/sql MaxIdleConns.
  -dbmaxopen=500: Sets the database/sql MaxOpenConns.
  -imguri="/cdn/uploads/": Set this to the full base uri of all images, for example on a remote CDN server or local relative virtual directory.
  -isdebug=false: Set to true to run the app in debug mode.  In debug, it may panic on some errors.
  -key="": Enables listening on 443 with -cert and -key files specified.  This must be a full path to the key .pem file. See http://golang.org/pkg/net/http/#ListenAndServeTLS for more information.
  -port=8080: Specify the port to listen to.

Very nice to have many options at CLI, and no documentation required - it's built into the flags package.

You can clearly see the defaults immediately.

With this type of documentation, I tend to setup all the defaults for common "development environments" that the team uses. We all have root/root access to our local databases. We all are using port 8080 for this particular web app during development, etc. That way, you just have to run:

go build
./wwwgo

And the app runs with all defaults - defaults that are documented. In production, just override the defaults. The built-in flag parsers will panic the application if any parameters are in the wrong format, which is also very nice.

like image 33
eduncan911 Avatar answered Oct 02 '22 13:10

eduncan911