cleanenv

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2019 License: MIT Imports: 11 Imported by: 764

README

Clean Env

Minimalistic configuration reader

GoDoc Go Report Card Coverage Status Build Status Release License

Overview

This is a simple configuration reading tool. It just does the following:

  • reads and parses configuration structure from the file
  • reads and overwrites configuration structure from environment variables

Content

Installation

To install the package run

go get -u github.com/ilyakaznacheev/cleanenv

Usage

The package is oriented to be simple in use and explicitness.

The main idea is to use a structured configuration variable instead of any sort of dynamic set of configuration fields like some libraries does, to avoid unnecessary type conversions and move the configuration through the program as a simple structure, not as an object with complex behavior.

There are just several actions you can do with this tool and probably only things you want to do with your config if your application is not too complicated.

  • read configuration file
  • read environment variables
  • read some environment variables again
Read Configuration

You can read a configuration file and environment variables in a single function call.

import github.com/ilyakaznacheev/cleanenv

type ConfigDatabase struct {
	Port     string `yml:"port" env:"PORT" env-default:"5432"`
	Host     string `yml:"host" env:"HOST" env-default:"localhost"`
	Name     string `yml:"name" env:"NAME" env-default:"postgres"`
	User     string `yml:"user" env:"USER" env-default:"user"`
	Password string `yml:"password" env:"PASSWORD"`
}

var cfg ConfigDatabase

err := cleanenv.ReadConfig("config.yml", &cfg)
if err != nil {
    ...
}

This will do the following:

  1. parse configuration file according to YAML format (yaml tag in this case);
  2. reads environment variables and overwrites values from the file with the values which was found in the environment (env tag);
  3. if no value was found on the first two steps, the field will be filled with the default value (env-default tag) if it is set.
Read Environment Variables Only

Sometimes you don't want to use configuration files at all, or you may want to use .env file format instead. Thus, you can limit yourself with only reading environment variables:

import github.com/ilyakaznacheev/cleanenv

type ConfigDatabase struct {
	Port     string `env:"PORT" env-default:"5432"`
	Host     string `env:"HOST" env-default:"localhost"`
	Name     string `env:"NAME" env-default:"postgres"`
	User     string `env:"USER" env-default:"user"`
	Password string `env:"PASSWORD"`
}

var cfg ConfigDatabase

err := cleanenv.ReadEnv(&cfg)
if err != nil {
    ...
}
Update Environment Variables

Some environment variables may change during the application run. To get the new values you need to mark these variables as updatable with the tag env-upd and then run the update function:

import github.com/ilyakaznacheev/cleanenv

type ConfigRemote struct {
	Port     string `env:"PORT" env-upd`
    Host     string `env:"HOST" env-upd`
    UserName string `env:"USERNAME"`
}

var cfg ConfigRemote

cleanenv.ReadEnv(&cfg)

// ... some actions in-between

err := cleanenv.UpdateEnv(&cfg)
if err != nil {
    ...
}

Here remote host and port may change in a distributed system architecture. Fields cfg.Port and cfg.Host can be updated in the runtime from corresponding environment variables. You can update them before the remote service call. Field cfg.UserName will not be changed after the initial read, though.

Description

You can get descriptions of all environment variables to use them in help documentation.

import github.com/ilyakaznacheev/cleanenv

type ConfigServer struct {
    Port     string `env:"PORT" env-description:"server port"`
    Host     string `env:"HOST" env-description:"server host"`
}

var cfg ConfigRemote

help, err := cleanenv.GetDescription(&cfg, nil)
if err != nil {
    ...
}

You will get the following:

Environment variables:
  PORT  server port
  HOST  server host

Model Format

Library uses tags to configure model of configuration structure. There are following tags:

  • env="<name>" - environment variable name (e.g. env="PORT");
  • env-upd - flag to mark a field as updatable. Run UpdateEnv(&cfg) to refresh updatable variables from environment;
  • env-default="<value>" - default value. If the field wasn't filled from the environment variable default value will be used instead;
  • env-separator="<value>" - custom list and map separator. If not set, the default separator , will be used;
  • env-description="<value>" - environment variable description.

Custom Functions

To enhance package abilities you can use some custom functions.

Custom Value Setter

To make custom type allows to set the value from the environment variable, you need to implement the Setter interface on the field level:

type MyField string

func (f MyField) SetValue(s string) error  {
	if s == "" {
		return fmt.Errorf("field value can't be empty")
	}
	f = MyField("my field is: "+ s)
	return nil
}

type Config struct {
    Field MyField `env="MY_VALUE"`
}

SetValue method should implement conversion logic from string to custom type.

Custom Value Update

You may need to execute some custom field update logic, e.g. for remote config load.

Thus, you need to implement the Updater interface on the structure level:

type Config struct {
	Field string
}

func (c *Config) Update() error {
    newField, err := SomeCustomUpdate()
    f.Field = newField
	return err
}

Supported File Formats

There are several most popular config file formats supported:

  • YAML
  • JSON
  • TOML

Examples

type Config struct {
	Port string `yml:"port" env:"PORT" env-default:"8080"`
	Host string `yml:"host" env:"HOST" env-default:"localhost"`
}

var cfg Config

err := ReadConfig("config.yml", &cfg)
if err != nil {
    ...
}

This code will try to read and parse the configuration file config.yml as the structure is described in the Config structure. Then it will overwrite fields from available environment variables (PORT, HOST).

For more details check the example directory.

Contribution

The tool is open-sourced under the MIT license.

If you will find some error, want to add something or ask a question - feel free to create an issue and/or make a pull request.

Any contribution is welcome.

Thanks

Big thanks to a project kelseyhightower/envconfig for inspiration.

Documentation

Overview

Package cleanenv gives you a single tool to read application configuration from several sources.

You can just prepare config structure and fill it from the config file and environment variables.

type Config struct {
	Port string `yml:"port" env:"PORT" env-default:"8080"`
	Host string `yml:"host" env:"HOST" env-default:"localhost"`
}

var cfg Config

ReadConfig("config.yml", &cfg)

Index

Examples

Constants

View Source
const (
	// DefaultSeparator is a defauld list and map separator character
	DefaultSeparator = ","
)

Variables

This section is empty.

Functions

func GetDescription

func GetDescription(cfg interface{}, headerText *string) (string, error)

GetDescription returns a description of environment variables. You can provide a custom header text.

Example

ExampleGetDescription builds a description text from structure tags

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter"`
		Two   float64 `env:"TWO" env-description:"second parameter"`
		Three string  `env:"THREE" env-description:"third parameter"`
	}

	var cfg config

	text, err := cleanenv.GetDescription(&cfg, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Environment variables:
  ONE int64
    	first parameter
  TWO float64
    	second parameter
  THREE string
    	third parameter
Example (Custom_header_text)

ExampleGetDescription_custom_header_text builds a description text from structure tags with custom header string

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter"`
		Two   float64 `env:"TWO" env-description:"second parameter"`
		Three string  `env:"THREE" env-description:"third parameter"`
	}

	var cfg config

	header := "Custom header text:"

	text, err := cleanenv.GetDescription(&cfg, &header)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Custom header text:
  ONE int64
    	first parameter
  TWO float64
    	second parameter
  THREE string
    	third parameter
Example (Defaults)

ExampleGetDescription_defaults builds a description text from structure tags with description of default values

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter" env-default:"1"`
		Two   float64 `env:"TWO" env-description:"second parameter" env-default:"2.2"`
		Three string  `env:"THREE" env-description:"third parameter" env-default:"test"`
	}

	var cfg config

	text, err := cleanenv.GetDescription(&cfg, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Environment variables:
  ONE int64
    	first parameter (default "1")
  TWO float64
    	second parameter (default "2.2")
  THREE string
    	third parameter (default "test")
Example (Variable_list)

ExampleGetDescription_variable_list builds a description text from structure tags with description of alternative variables

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		FirstVar int64 `env:"ONE,TWO,THREE" env-description:"first found parameter"`
	}

	var cfg config

	text, err := cleanenv.GetDescription(&cfg, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Environment variables:
  ONE int64
    	first found parameter
  TWO int64 (alternative to ONE)
    	first found parameter
  THREE int64 (alternative to ONE)
    	first found parameter

func ReadConfig

func ReadConfig(path string, cfg interface{}) error

ReadConfig reads configuration file and parses it depending on tags in structure provided. Then it reads and parses

Example:

type ConfigDatabase struct {
	Port     string `yml:"port" env:"PORT" env-default:"5432"`
	Host     string `yml:"host" env:"HOST" env-default:"localhost"`
	Name     string `yml:"name" env:"NAME" env-default:"postgres"`
	User     string `yml:"user" env:"USER" env-default:"user"`
	Password string `yml:"password" env:"PASSWORD"`
}

var cfg ConfigDatabase

err := cleanenv.ReadConfig("config.yml", &cfg)
if err != nil {
    ...
}

func ReadEnv

func ReadEnv(cfg interface{}) error

ReadEnv reads environment variables into the structure.

Example

ExampleReadEnv reads environment variables or default values into the structure

package main

import (
	"fmt"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		Port     string `env:"PORT" env-default:"5432"`
		Host     string `env:"HOST" env-default:"localhost"`
		Name     string `env:"NAME" env-default:"postgres"`
		User     string `env:"USER" env-default:"user"`
		Password string `env:"PASSWORD"`
	}

	var cfg config

	os.Setenv("PORT", "5050")
	os.Setenv("NAME", "redis")
	os.Setenv("USER", "tester")
	os.Setenv("PASSWORD", "*****")

	cleanenv.ReadEnv(&cfg)
	fmt.Printf("%+v\n", cfg)

}
Output:

{Port:5050 Host:localhost Name:redis User:tester Password:*****}

func UpdateEnv

func UpdateEnv(cfg interface{}) error

UpdateEnv rereads (updates) environment variables in the structure.

Example

ExampleUpdateEnv updates variables in the configuration structure. Only variables with `env-upd:""` tag will be updated

package main

import (
	"fmt"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One int64 `env:"ONE"`
		Two int64 `env:"TWO" env-upd:""`
	}

	var cfg config

	// set environment variables
	os.Setenv("ONE", "1")
	os.Setenv("TWO", "2")

	// read environment variables into the structure
	cleanenv.ReadEnv(&cfg)
	fmt.Printf("%+v\n", cfg)

	// update environment variables
	os.Setenv("ONE", "11")
	os.Setenv("TWO", "22")

	// update only updatable environment variables in the structure
	cleanenv.UpdateEnv(&cfg)
	fmt.Printf("%+v\n", cfg)

}
Output:

{One:1 Two:2}
{One:1 Two:22}

Types

type Setter

type Setter interface {
	SetValue(string) error
}

Setter is an interface for a custom value setter.

To implement a custom value setter you need to add a SetValue function to your type that will receive a string raw value:

type MyField string

func (f *MyField) SetValue(s string) error {
	if s == "" {
		return fmt.Errorf("field value can't be empty")
	}
	*f = MyField("my field is: " + s)
	return nil
}

type Updater

type Updater interface {
	Update() error
}

Updater gives an ability to implement custom update function for a field or a whole structure

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL