8000 GitHub - evilwire/go-env: Marshals objects (pointers, uints, and nested structs) from environment variables.
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

evilwire/go-env

Repository files navigation

Golang Marshaler from Environment Variables

Build Status GoDoc Go Report Card codecov

Golang (1.5+) package for marshalling objects from environment variable values. There are many like packages. The one that inspires this API the most is the json package. The idea is that configuration objects are stored as environment variables, especially when running as a containerised application.

Example

The following is a full and well contrived application that showcases how to use the API to retrieve data from environments. The application

  • retrieves a list of things to say and wait time (via config)
  • for everything to be said, it prints a simple statement

Disclaimer: this is a silly toy application. Don't use this as an example of good application design. It isn't.

package main


import (
    "fmt"
    "github.com/evilwire/go-env"
    "time"
)


type SomeConfig struct {
        User string `env:"USER_NAME"`
        
        Application struct {
                WaitDuration time.Duration `env:"WAIT_DURATION"`
                
                // this is best obtained from flags, but you can equally
                // retrieve this from environment variables
                ThingsToSay []string `env:"THINGS_TO_SAY"`
        } `env:"APP_"`
}


// Sets up my application by reading the configs
func setup(env goenv.EnvReader) (*SomeConfig, error) {
        // create a Marshaler using our lovely default, which knows
        // how to marshal a set of things
        marshaller := goenv.DefaultEnvMarshaler{
                env,
        }
        
        // instantiate an empty config 
        config := SomeConfig{} 
        err := marshaller.Unmarshal(&config)
        if err != nil {
            return nil, err
        }
        
        return &config, nil
}


func main() {
        config, err := setup(goenv.NewOsEnvReader())
        if err != nil {
                panic(err)
        }
        
        for _, line := range config.Application.ThingsToSay {
                fmt.Printf("%s says: ", config.User)
                fmt.Println(line)
                time.Sleep(config.Application.WaitDuration)
        }
        
        fmt.Println("We're done!")
}

Compile your application, say silly-app, and run your application

USER_NAME='Michael Bluth' \
APP_WAIT_DURATION=2s \
APP_THINGS_TO_SAY='hiya,how are you,bye' /path/to/silly-app

Customising the UnmarshalEnv method

One of the cases that I encounter is using AWS KMS to manage secrets. For example, you may want to store KMS-encrypted credentials in a distributed version control source such GitHub, and passed into your application directly via environment variables.

We want to use the following function to decrypt KMS-encrypted secrets

package whatever

import (
        "github.com/aws/aws-sdk-go/service/kms"
        "github.com/aws/aws-sdk-go/service/kms/kmsiface"
        "encoding/base64"
)


// Uses KMS to decrypt an encrypted, base64 encoded secret (string) 
// by base64 decoding and KMS decrypting the bugger
func KMSDecrypt(secret string, kmsClient kmsiface.KMSAPI) (string, error) {
        b64Secret, err := base64.StdEncoding.DecodeString(secret)
        if err != nil {
                // handle
                return "", err
        }
        response, err := kmsClient.Decrypt(&kms.DecryptInput {
                CiphertextBlob: b64Secret,
                
                // additional context???
        })
        if err != nil {
            return "", err
        }
        
        return string(response.Plaintext), nil
}

Let's make a custom marshal method that ingests KMS-encrypted

package whatever

import (
    "github.com/evilwire/go-env"
    "github.com/aws/aws-sdk-go/service/kms"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/aws"
)

type KMSEncryptedConfig struct {
        Username string `env:"USER"`
        Password string
        
        // and other fields that one might be able
        // to get from things
}


func (config *KMSEncryptedConfig) MarshalEnv(env goenv.EnvReader) error {
        tempConfig := struct {
                KMSEncryptedConfig
                KMSPassword string `env:"KMS_PASSWORD"`
        }{}
        
        marshaller := goenv.DefaultEnvMarshaler{ env }
        err := marshaller.Unmarshal(&tempConfig)
        if err != nil {
                return err
        }
        
        password, err := KMSDecrypt(tempConfig.KMSPassword, &kms.New(session.New(aws.Config{
            // configuration of some sort
        })))
        if err != nil {
                return err
        }
        
        config.Username = tempConfig.Username
        config.Password = password
        // more copying...
        
        return nil
}

Now you can write an application and accepts KMS passwords and not have to worry:

package main


import (
        "whatever"
        "github.com/evilwire/go-env"
)


type Config struct {
        DbCredentials *whatever.KMSEncryptedConfig `env:"DB_"`
        
        // other types of configs
}


func doStuff(config *Config) error {
        // do stuff
        
        return nil
}


func setup(env goenv.EnvReader) (*Config, error) {
        config := Config {}
        marshaller := goenv.DefaultEnvMarshaler{ env }
        
        err := marshaller.Unmarshal(&config)
        if err != nil {
                return nil, err
        }
        
        // does setuppy things...
        
        return config, nil
}


func main() {
        config, err := setup(goenv.NewOsEnvReader())
        if err != nil {
                panic(err)
        }
        
        // does stuff with that config
        panic(doStuff(config))
}

Now compile your application as, say app, and run the application as:

DB_KMS_PASSWORD=ABCD1234abcd1234 \
DB_USER=mbluth \
#... \
app

About

Marshals objects (pointers, uints, and nested structs) from environment variables.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

0