Git Product home page Git Product logo

snek's Introduction

Snek

cacois Coverage Status Go Report Card License: MIT go.dev reference

Snek is a very simple environment variable-based application config management system, for the Go programming language. Snek is designed for docker/kubernetes use cases. Very small fangs.

Why?

Go already has some great config management tools, like Viper. Powerful...but complex. Managing precedence between config sources, many file formats, aliases...sometimes your needs are simpler.

Like many developers, I find myself working in a Docker/Kubernetes world more often than not. So, who needs config files? All I need is environment variables and/or a ConfigMap. This simplifies config management from within my app - all I really need is something that looks for my config values in environment variables, and allows me to set default values in case the desired environment variables are empty. Fin.

I'm tired of writing this (admittedly simple) logic into each of my apps, so here we are. :)

Usage

Start by importing the module:

$ go get "github.com/cacois/snek"

Snek is really simple. You can do two things. First, if you want to, you can set default values for a particular configuration parameter:

snek.Default("MY_ENV_VAR", "mydefaultvalue")

Then you can read your configuration values from anywhere in your app:

value := snek.Get("MY_ENV_VAR")

This will first look for and return the value of the environment variable MY_ENV_VAR. If this environment variable has not been set, snek will look for any default value you defined for MY_ENV_VAR and return that. If neither has been set, it will return an empty string.

One more option is to use snek.GetOrError() instead of snek.Get():

value, err := snek.GetOrError("MY_ENV_VAR")
if err != nil {
    // do something about it
}

This function behaves the exact same way as snek.Get(), except instead of returning an empty string when neither the specified environment variable or a default value has been defined, it throws an error.

Patterns

Inline Config Value Access

The easiest way to use snek is just to set all your defaults in your main.go file, in the init() or main() function:

main.go:

package main

func init() {
    snek.Default("CONFIG_VAL_1", "somevalue")
    snek.Default("CONFIG_VAL_2", "anothervalue")
}
...

Its convenient to set the defaults for all config values in your app in a single place, to easily keep track of them.

You can then pull your config values from anywhere in your app, except from init() functions or package-level variables, since those pieces of code will be executed before the init() function in main.go - meaning you will try to access your variables before your defaults have been set. (If you don't set or care about default values, this warning is irrelevant) I refer to this as "inline" variable access, since it means I'm accessing the values from within my business logic, inside functions:

someotherfile.go:

package somepackage

func SomeFunction() {
    // if env var CONFIG_VAL_1 is empty, default value "somevalue" will be returned
    value1 := snek.Get("CONFIG_VAL_1") 
}

Package-level Config Value Access

Sometimes, I like to be able to also set my config values in package-level variables:

somefile.go:

package somepackage

var (
    value1 := snek.Get("CONFIG_VAL_1") // package-level variable
)

func SomeFunction() {
    fmt.Sprintf("value1: %s", value1)
}

This presents an interesting challenge, because package-level variable definitions are executed very early in Go - before your entrypoint main() function or even the init() function in your main.go file. This means all my default values are not yet set, value1 above is set as "". So, to be able to assign the variables in this way, I tend to make a package called config with a single file named config.go:

config.go:

package config

func init() {
    snek.Default("CONFIG_VAL_1", "value1")
    snek.Default("CONFIG_VAL_2", "value2")
    snek.Default("CONFIG_VAL_3", "value3")
}

func Init() {
    // dummy function to allow me to force early execution of
    // the above init() function
}

I then put a big list of default values in the init() function. To make sure this function is executed to set the defaults before any other package-level code, I add an empty Init() function and call it early in the main() function in main.go (you have to call something in the config package, or Go will not allow you to import it):

main.go:

package main

import config

func main() {
    config.Init()
}

A little legwork, but problem solved.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.