conf

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2024 License: Apache-2.0 Imports: 9 Imported by: 1

README

Go Report Card GitHub Actions Go Reference

conf

Package conf provides a set of utilities for mapping configuration settings from env vars, flags and secret managers to struct fields.

import "github.com/fritzkeyzer/conf"

Features

Load struct fields from environment variables

var Config struct {
    DBConn string `env:"DB_CONN"`
}

_ = conf.LoadEnv(&Config)

Load struct fields from CLI flags

var Config struct {
    DBConn string `flag:"db-conn"`
}

_ = conf.LoadFlags(&Config)

Load struct fields from a secret manager

var Config struct {
    DBConn string `secret:"db-conn"`
}

_ = conf.LoadSecrets(&Config, secretMgr) // where secretMgr implements the SecretSource interface

Utilities

Parse flags from []string, eg: os.Args

args := []string{"nonsense", "--xyz=abc", "nonsense", "-v"}

// get value, found from []string
value, found := GetFlag("--xyz", args) // value = "abc", found = true

// eg:
_, verbose := GetFlag("-v", args) // verbose = true

Print a config to stdout

type Config struct {
    Host    string `env:"HOST" flag:"--host"`
    Verbose bool   `flag:"-v"`
    Count   int    `flag:"--count"`
    DB      struct {
    Name string `env:"DB_NAME"`
    User string `env:"DB_USER" secret:"db-user"`
    Pass string `env:"DB_PASS" secret:"db-pass"`
}

conf.Print(&cfg) // notice how the secret fields are masked with ***

// Output:
// ---------------------------
//  Host      = "localhost"
//  Verbose   = false
//  DB
//    .Name   = "app"
//    .User   = "user"
//    .Pass   ***
// ---------------------------

Flatten struct fields and iterate over them, eg: to export to a .env file

fields, _ := conf.FlattenStructFields(&cfg)

envFile := ""
for _, field := range fields {
    envVar, e := field.EnvVar()
    if !e {
        continue
    }
    
    envVal, _ := field.ExportValue() // exports values in a way that is suitable for env vars and for loading back into the struct
    envFile += "export " + envVar + "=" + envVal + "\n"
}

Example

Example from examples/main.go

package main

import (
	"fmt"
	"os"

	"github.com/fritzkeyzer/conf"
)

type Config struct {
	Host    string `env:"HOST" flag:"--host"`
	Verbose bool   `flag:"-v"`
	Count   int    `flag:"--count"`
	DB      struct {
		Name string `env:"DB_NAME"`
		User string `env:"DB_USER" secret:"db-user"`
		Pass string `env:"DB_PASS" secret:"db-pass"`
	}
}

// main demonstrates various functions of the conf package
//   - LoadEnv loads fields from environment variables
//   - LoadFlags loads fields from command line flags
//   - LoadSecrets loads fields from a secret manager
//   - Print prints the config to stdout
func main() {
	// for demo purposes, we set the env vars here
	os.Setenv("HOST", "localhost")
	os.Setenv("DB_NAME", "app")
	os.Setenv("DB_USER", "user from env")
	os.Setenv("DB_PASS", "pass from env")

	var cfg Config

	if err := conf.LoadEnv(&cfg); err != nil {
		panic(err)
	}

	// fields can be overridden by flags, eg: host, verbose or count
	if err := conf.LoadFlags(&cfg); err != nil {
		panic(err)
	}

	// fields can be loaded from a secret manager
	if err := conf.LoadSecrets(&cfg, &SecretManager{}); err != nil {
		panic(err)
	}

	// notice how the secret fields are masked with ***
	conf.Print(&cfg)

	// Output:
	// ---------------------------
	//  Host      = "localhost"
	//  Verbose   = false
	//  DB
	//    .Name   = "app"
	//    .User   = "user"
	//    .Pass   ***
	// ---------------------------

	// other features include flattening the struct fields
	fields, _ := conf.FlattenStructFields(&cfg)

	// this way you can iterate over the fields and do something with them
	// eg: exporting the values to a .env file
	envFile := ""
	for _, field := range fields {
		envVar, e := field.EnvVar()
		if !e {
			continue
		}

		envVal, _ := field.ExportValue()
		envFile += "export " + envVar + "=" + envVal + "\n"
	}

	fmt.Println(envFile)
	// Output:
	//export HOST=localhost
	//export DB_NAME=app
	//export DB_USER=user from secret manager
	//export DB_PASS=secret password 1337
}

// SecretManager is a mock secret manager, for demo purposes
type SecretManager struct{}

func (sm *SecretManager) Load(key string) ([]byte, bool, error) {
	if key == "db-user" {
		return []byte("user from secret manager"), true, nil
	}

	if key == "db-pass" {
		return []byte("secret password 1337"), true, nil
	}

	return nil, false, nil
}

Documentation

Overview

Package conf provides a set of utilities for mapping configuration settings (from env vars, flags and secret managers) to struct fields.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetFlag

func GetFlag(flag string, args []string) (val string, found bool)

GetFlag is a utility to extract a flag from a slice of CLI args. It returns the value of the flag and a boolean indicating whether the flag was found. For example, args could be os.Args[1:]. flag should include the prefix, eg: "--verbose" or "-v" GetFlag supports the following formats:

flag=value
flag="value"
flag='value'
flag value
flag "value"
flag 'value'
Example
args := []string{"nonsense", "--xyz=abc", "nonsense", "-v"}

xyz, _ := GetFlag("--xyz", args)
_, verbose := GetFlag("-v", args)

fmt.Printf("xyz = %q, verbose = %v", xyz, verbose)
Output:

xyz = "abc", verbose = true

func Load added in v1.1.0

func Load[T any](cfg LoadCfg) (T, error)

Load config from multiple sources. T should be a struct with tagged fields:

  • secrets: `secret:mySecretValue`
  • env vars: `env:MY_ENV_VAR`
  • CLI flags: `flag:--flag`

Sources are loaded in the following order:

  1. First load secrets from SecretsLoader (if not nil)
  2. Then environment variables - which will override secrets
  3. Finally command line flags - which override both secrets and env vars

func LoadEnv

func LoadEnv(ptr any) error

LoadEnv recursively scans struct fields for the env tag then sets the values from the corresponding env var. Eg:

type Config struct {
	Host string `env:"HOST"`
}

func LoadFlags

func LoadFlags(ptr any) error

LoadFlags recursively scans struct fields for the `flag` tag then sets the values from CLI flags. Eg:

type Config struct {
	Host    string `flag:"--host"`
	Verbose bool   `flag:"-v"`
}

func LoadSecrets

func LoadSecrets(ptr any, source SecretsLoader) error

LoadSecrets recursively scans struct fields for the secret tag then sets the values from the secret SecretsLoader. Eg:

type Config struct {
	Host string `secret:"host"`
}

func MustLoad added in v1.1.0

func MustLoad[T any](cfg LoadCfg) T

MustLoad is a wrapper for Load which will panic if Load returns an error.

Example
package main

import (
	"os"
	"strings"

	"github.com/fritzkeyzer/conf"
)

func main() {
	type Cfg struct {
		Debug bool   `flag:"--debug"`           // enable debug logs
		Host  string `flag:"--host" env:"HOST"` // host URL
		DB    struct {
			User string `env:"DB_USER" flag:"--db-user"`
			Pass string `env:"DB_PASS" flag:"--db-pass" secret:"db-pass"`
		}
	}

	// fake env vars and cli flags
	_ = os.Setenv("HOST", "localhost:1111")
	_ = os.Setenv("DB_USER", "not_the_root_user")
	fakeCliFlags := " --debug --host=localhost:8888" // notice how the flag here will override the env var
	os.Args = strings.Split(fakeCliFlags, " ")

	// one-liner loads the config
	cfg := conf.MustLoad[Cfg](conf.LoadCfg{Env: true, Flags: true})

	conf.Print(cfg)
}
Output:

-----------------------------------
  Debug     = true
  Host      = "localhost:8888"
  DB
    .User   = "not_the_root_user"
    .Pass   *** (len=0)
-----------------------------------

func Print

func Print(ptr any)

Print wraps PrintToString and prints the result to stdout. Example output:

Host      = "localhost"
Verbose   = false
DB
  .Name   = "app"
  .User   = "user"
  .Pass   ***

func PrintToString

func PrintToString(ptr any) string

PrintToString returns a string representation of the config struct. Secrets are masked. Example output:

Host      = "localhost"
Verbose   = false
DB
  .Name   = "app"
  .User   = "user"
  .Pass   ***

Types

type Field added in v1.0.3

type Field struct {
	// contains filtered or unexported fields
}

Field represents a struct field

func FlattenStructFields added in v1.0.3

func FlattenStructFields(ptr any) ([]Field, error)

FlattenStructFields returns a flat slice of Field from recursively traversing the struct fields of v.

  • unexported fields are omitted
  • fields marked with an env, flag or secret tag are included, but their children are not

func (*Field) EnvVar added in v1.0.3

func (f *Field) EnvVar() (string, bool)

EnvVar returns the `env` tag value and a bool indicating if the field has the `env` tag.

func (*Field) ExportValue added in v1.0.3

func (f *Field) ExportValue() (string, error)

ExportValue returns the value of the field as a string.

  • []byte fields are base64 encoded
  • string fields are not pre-processed
  • all other types marshalled to JSON

func (*Field) FlagName added in v1.0.3

func (f *Field) FlagName() (string, bool)

FlagName returns the `flag` tag value and a bool indicating if the field has the `flag` tag.

func (*Field) SecretKey added in v1.0.3

func (f *Field) SecretKey() (string, bool)

SecretKey returns the `secret` tag value and a bool indicating if the field has the `secret` tag.

type LoadCfg added in v1.1.0

type LoadCfg struct {
	Env           bool
	Flags         bool
	SecretsLoader SecretsLoader
}

type SecretsLoader added in v1.1.0

type SecretsLoader interface {
	// Load a secret from the source. Returns the secret value, a boolean indicating if the secret was found and an error.
	// NOTE: Load should not return an error if the secret was not found, but should instead return "", false, nil.
	Load(key string) ([]byte, bool, error)
}

SecretsLoader interface allows any secret manager to be used, by wrapping it in a type that implements this interface.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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