bconf

package module
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2023 License: Apache-2.0 Imports: 10 Imported by: 1

README

bconf: better / builder configuration for go

License GoDoc Go Report Card Build Status codecov.io

bconf is a configuration framework that makes it easy to define, load, and validate application configuration values.

go get github.com/rheisen/bconf
Why bconf

bconf provides tooling to write your configuration package by package. With bconf, configuration lives right alongside the code that needs it. This also makes it so that configuration is more easily re-used and composible by multiple applications (just like your packages should be).

bconf accomplishes this with bconf.FieldSets, which provide a namespace and logical grouping for related configuration. Independent packages define their bconf.FieldSets, and then application executables can attach them to a bconf.AppConfig, which provides a unified structure for loading and retrieving configuration values.

Within bconf.FieldSets, you define bconf.Fields, with each field defining the expected format and behavior of a configuration value.

Check out the documentation and introductory examples below, and see if bconf is right for your project!

Supported Configuration Sources
  • Environment (bconf.EnvironmentLoader)
  • Flags (bconf.FlagLoader)
  • JSON files (bconf.JSONFileLoader)
  • Overrides (setter functions)

In Progress

  • YAML files (bconf.YAMLFileLoader)
  • TOML files (bconf.TOMLFileLoader)
Getting Values from bconf.AppConfig
  • GetField(fieldSetKey, fieldKey string) (*bconf.Field, error)
  • GetString(fieldSetKey, fieldKey string) (string, error)
  • GetStrings(fieldSetKey, fieldKey string) ([]string, error)
  • GetInt(fieldSetKey, fieldKey string) (int, error)
  • GetInts(fieldSetKey, fieldKey string) ([]int, error)
  • GetBool(fieldSetKey, fieldKey string) (bool, error)
  • GetBools(fieldSetKey, fieldKey string) ([]bool, error)
  • GetTime(fieldSetKey, fieldKey string) (time.Time, error)
  • GetTimes(fieldSetKey, fieldKey string) ([]time.Time, error)
  • GetDuration(fieldSetKey, fieldKey string) (time.Duration, error)
  • GetDurations(fieldSetKey, fieldKey string) ([]time.Duration, error)
Additional Features
  • Ability to generate default configuration values with the bconf.Field DefaultGenerator parameter
  • Ability to define custom configuration value validation with the bconf.Field Validator parameter
  • Ability to conditionally load a bconf.FieldSet by defining bconf.LoadConditions
  • Ability to conditionally load a bconf.Field by defining bconf.LoadConditions
  • Ability to get a safe map of configuration values from the bconf.AppConfig ConfigMap() function
    • (the configuration map will obfuscate values from fields with Sensitive parameter set to true)
  • Ability to reload field-sets and individual fields via the bconf.AppConfig
Limitations
  • No support for watching / automatically updating configuration values
Example

Below is an example of a bconf.AppConfig defined first with builders, and then with structs. Below these code blocks the behavior of the example is discussed.

configuration := bconf.NewAppConfig(
    "external_http_api",
    "HTTP API for user authentication and authorization",
)

_ = configuration.SetLoaders(
    &bconf.EnvironmentLoader{KeyPrefix: "ext_http_api"},
    &bconf.FlagLoader{},
)

_ = configuration.AddFieldSets(
    bconf.NewFieldSetBuilder().Key("app").Fields(
        bconf.NewFieldBuilder().
            Key("id").Type(bconf.String).
            Description("Application identifier").
            DefaultGenerator(
                func() (any, error) {
                    return fmt.Sprintf("%s", uuid.NewV4().String()), nil
                },
            ).Create(),
        bconf.FB(). // FB() is a shorthand function for NewFieldBuilder()
                Key("session_secret").Type(bconf.String).
                Description("Application secret for session management").
                Sensitive().Required().
                Validator(
                func(fieldValue any) error {
                    secret, _ := fieldValue.(string)

                    minLength := 20
                    if len(secret) < minLength {
                        return fmt.Errorf(
                            "expected string of minimum %d characters (len=%d)",
                            minLength,
                            len(secret),
                        )
                    }

                    return nil
                },
            ).Create(),
    ).Create(),
    bconf.FSB().Key("log").Fields( // FSB() is a shorthand function for NewFieldSetBuilder()
        bconf.FB().
            Key("level").Type(bconf.String).Default("info").
            Description("Logging level").
            Enumeration("debug", "info", "warn", "error").Create(),
        bconf.FB().
            Key("format").Type(bconf.String).Default("json").
            Description("Logging format").
            Enumeration("console", "json").Create(),
        bconf.FB().
            Key("color_enabled").Type(bconf.Bool).Default(true).
            Description("Colored logs when format is 'console'").
            Create(),
    ).Create(),
)

// Register with the option to handle --help / -h flag set to true
if errs := configuration.Register(true); len(errs) > 0 {
    // handle configuration load errors
}

// returns the log level found in order of: default -> environment -> flag -> user override
// (based on the loaders set above).
logLevel, err := configuration.GetString("log", "level")
if err != nil {
    // handle retrieval error
}

fmt.Printf("log-level: %s", logLevel)
configuration := bconf.NewAppConfig(
    "external_http_api",
    "HTTP API for user authentication and authorization",
)

_ = configuration.SetLoaders(
    &bconf.EnvironmentLoader{KeyPrefix: "ext_http_api"},
    &bconf.FlagLoader{},
)

_ = configuration.AddFieldSets(
    &bconf.FieldSet{
        Key: "app",
        Fields: bconf.Fields{
            {
                Key:         "id",
                Type:        bconf.String,
                Description: "Application identifier",
                DefaultGenerator: func() (any, error) {
                    return uuid.NewV4().String(), nil
                },
            },
            {
                Key:         "session_secret",
                Type:        bconf.String,
                Description: "Application secret for session management",
                Sensitive:   true,
                Required:    true,
                Validator: func(fieldValue any) error {
                    secret, _ := fieldValue.(string)

                    minLength := 20
                    if len(secret) < minLength {
                        return fmt.Errorf(
                            "expected string of minimum %d characters (len=%d)",
                            minLength,
                            len(secret),
                        )
                    }

                    return nil
                },
            },
        },
    },
    &bconf.FieldSet{
        Key: "log",
        Fields: bconf.Fields{
            {
                Key:         "level",
                Type:        bconf.String,
                Description: "Logging level",
                Default:     "info",
                Enumeration: []any{"debug", "info", "warn", "error"},
            },
            {
                Key:         "format",
                Type:        bconf.String,
                Description: "Logging format",
                Default:     "json",
                Enumeration: []any{"console", "json"},
            },
            {
                Key:         "color_enabled",
                Type:        bconf.Bool,
                Description: "Colored logs when format is 'console'",
                Default:     true,
            },
        },
    },
)

// Register with the option to handle --help / -h flag set to true
if errs := configuration.Register(true); len(errs) > 0 {
    // handle configuration load errors here
}

// returns the log level found in order of: default -> environment -> flag -> user override
// (based on the loaders set above).
logLevel, err := configuration.GetString("log", "level")
if err != nil {
    // handle retrieval error
}

fmt.Printf("log-level: %s", logLevel)

In both of the code blocks above, a bconf.AppConfig is defined with two field-sets (which group configuration related to the application and logging in this case), and registered with help flag parsing.

If this code was executed in a main() function, it would print the log level picked up by the configuration from the flags or run-time environment before falling back on the defined default value of "info".

If this code was executed inside the main() function and passed a --help or -h flag, it would print the following output:

Usage of 'external_http_api':
HTTP API for user authentication and authorization

Required Configuration:
        app_session_secret string
                Application secret for session management
                Environment key: 'EXT_HTTP_API_APP_SESSION_SECRET'
                Flag argument: '--app_session_secret'
Optional Configuration:
        app_id string
                Application identifier
                Default value: <generated-at-run-time>
                Environment key: 'EXT_HTTP_API_APP_ID'
                Flag argument: '--app_id'
        log_color_enabled bool
                Colored logs when format is 'console'
                Default value: 'true'
                Environment key: 'EXT_HTTP_API_LOG_COLOR_ENABLED'
                Flag argument: '--log_color_enabled'
        log_format string
                Logging format
                Accepted values: ['console', 'json']
                Default value: 'json'
                Environment key: 'EXT_HTTP_API_LOG_FORMAT'
                Flag argument: '--log_format'
        log_level string
                Logging level
                Accepted values: ['debug', 'info', 'warn', 'error']
                Default value: 'info'
                Environment key: 'EXT_HTTP_API_LOG_LEVEL'
                Flag argument: '--log_level'

This is a simple example where all the configuration code is in one place, but it doesn't need to be!

To view more examples, including a real-world example showcasing how configuration can live alongside package code, please visit github.com/rheisen/bconf-examples.

Roadmap Features / Improvements
  • Additional -h / --help options
  • Additional configuration loaders

Documentation

Index

Constants

View Source
const (
	Bool      = "bool"
	Bools     = "[]bool"
	String    = "string"
	Strings   = "[]string"
	Int       = "int"
	Ints      = "[]int"
	Float     = "float64"
	Floats    = "[]float64"
	Time      = "time.Time"
	Times     = "[]time.Time"
	Duration  = "time.Duration"
	Durations = "[]time.Duration"
)

Variables

This section is empty.

Functions

func FieldTypes added in v0.3.0

func FieldTypes() []string

Types

type AppConfig

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

func NewAppConfig

func NewAppConfig(appName, appDescription string) *AppConfig

func (*AppConfig) AddField

func (c *AppConfig) AddField(fieldSetKey string, field *Field) []error

func (*AppConfig) AddFieldSet

func (c *AppConfig) AddFieldSet(fieldSet *FieldSet) []error

func (*AppConfig) AddFieldSets added in v0.2.0

func (c *AppConfig) AddFieldSets(fieldSets ...*FieldSet) []error

func (*AppConfig) AppDescription

func (c *AppConfig) AppDescription() string

func (*AppConfig) AppName

func (c *AppConfig) AppName() string

func (*AppConfig) ConfigMap

func (c *AppConfig) ConfigMap() map[string]map[string]any

func (*AppConfig) GetBool

func (c *AppConfig) GetBool(fieldSetKey, fieldKey string) (bool, error)

func (*AppConfig) GetBools

func (c *AppConfig) GetBools(fieldSetKey, fieldKey string) ([]bool, error)

func (*AppConfig) GetDuration

func (c *AppConfig) GetDuration(fieldSetKey, fieldKey string) (time.Duration, error)

func (*AppConfig) GetDurations

func (c *AppConfig) GetDurations(fieldSetKey, fieldKey string) ([]time.Duration, error)

func (*AppConfig) GetField

func (c *AppConfig) GetField(fieldSetKey, fieldKey string) (*Field, error)

func (*AppConfig) GetFieldSetFieldKeys

func (c *AppConfig) GetFieldSetFieldKeys(fieldSetKey string) ([]string, error)

func (*AppConfig) GetFieldSetKeys

func (c *AppConfig) GetFieldSetKeys() []string

func (*AppConfig) GetInt

func (c *AppConfig) GetInt(fieldSetKey, fieldKey string) (int, error)

func (*AppConfig) GetInts

func (c *AppConfig) GetInts(fieldSetKey, fieldKey string) ([]int, error)

func (*AppConfig) GetString

func (c *AppConfig) GetString(fieldSetKey, fieldKey string) (string, error)

func (*AppConfig) GetStrings

func (c *AppConfig) GetStrings(fieldSetKey, fieldKey string) ([]string, error)

func (*AppConfig) GetTime

func (c *AppConfig) GetTime(fieldSetKey, fieldKey string) (time.Time, error)

func (*AppConfig) GetTimes

func (c *AppConfig) GetTimes(fieldSetKey, fieldKey string) ([]time.Time, error)

func (*AppConfig) HelpString

func (c *AppConfig) HelpString() string

func (*AppConfig) LoadField

func (c *AppConfig) LoadField(fieldSetKey, fieldKey string) []error

func (*AppConfig) LoadFieldSet

func (c *AppConfig) LoadFieldSet(fieldSetKey string) []error

func (*AppConfig) Register

func (c *AppConfig) Register(handleHelpFlag bool) []error

Register loads all defined field sets and optionally checks for and handles the help flag -h and --help.

func (*AppConfig) SetField

func (c *AppConfig) SetField(fieldSetKey, fieldKey string, fieldValue any) error

func (*AppConfig) SetLoaders

func (c *AppConfig) SetLoaders(loaders ...Loader) []error

type EnvironmentLoader

type EnvironmentLoader struct {
	KeyPrefix string
}

func NewEnvironmentLoader added in v0.3.0

func NewEnvironmentLoader() *EnvironmentLoader

func NewEnvironmentLoaderWithKeyPrefix added in v0.3.0

func NewEnvironmentLoaderWithKeyPrefix(keyPrefix string) *EnvironmentLoader

func (*EnvironmentLoader) Clone

func (*EnvironmentLoader) CloneLoader added in v0.4.0

func (l *EnvironmentLoader) CloneLoader() Loader

func (*EnvironmentLoader) Get

func (l *EnvironmentLoader) Get(fieldSetKey, fieldKey string) (string, bool)

func (*EnvironmentLoader) GetMap added in v0.4.0

func (l *EnvironmentLoader) GetMap(fieldSetKey string, fieldKeys []string) map[string]string

func (*EnvironmentLoader) HelpString

func (l *EnvironmentLoader) HelpString(fieldSetKey, fieldKey string) string

func (*EnvironmentLoader) Name

func (l *EnvironmentLoader) Name() string

type Field

type Field struct {

	// Validator defines a function that runs during validation to check a value against validity constraints
	Validator func(value any) error
	// DefaultGenerator defines a function that creates a base value for a field
	DefaultGenerator func() (any, error)
	// Default defines a base value for a field
	Default any

	// Key is a required field that defines the field lookup value
	Key string
	// Type is a required field that defines the type of value the field contains
	Type string
	// Description defines a summary of the field contents
	Description string
	// Enumeration defines a list of acceptable inputs for the field value
	Enumeration []any
	// LoadConditions defines the conditions required for a field to load values
	LoadConditions LoadConditions

	// Required defines whether a field value must be set in order for the field to be valid
	Required bool
	// Sensitive identifies the field value as sensitive
	Sensitive bool
	// contains filtered or unexported fields
}

Field is a data structure that provides context for a configuration value

func (*Field) Clone added in v0.2.0

func (f *Field) Clone() *Field

type FieldBuilder added in v0.3.0

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

func FB added in v0.3.0

func FB() *FieldBuilder

func NewFieldBuilder added in v0.3.0

func NewFieldBuilder() *FieldBuilder

func (*FieldBuilder) Create added in v0.3.0

func (b *FieldBuilder) Create() *Field

func (*FieldBuilder) Default added in v0.3.0

func (b *FieldBuilder) Default(value any) *FieldBuilder

func (*FieldBuilder) DefaultGenerator added in v0.3.0

func (b *FieldBuilder) DefaultGenerator(value func() (any, error)) *FieldBuilder

func (*FieldBuilder) Description added in v0.3.0

func (b *FieldBuilder) Description(value string) *FieldBuilder

func (*FieldBuilder) Enumeration added in v0.3.0

func (b *FieldBuilder) Enumeration(value ...any) *FieldBuilder

func (*FieldBuilder) Key added in v0.3.0

func (b *FieldBuilder) Key(value string) *FieldBuilder

func (*FieldBuilder) LoadConditions added in v0.5.0

func (b *FieldBuilder) LoadConditions(value ...LoadCondition) *FieldBuilder

func (*FieldBuilder) Required added in v0.3.0

func (b *FieldBuilder) Required() *FieldBuilder

func (*FieldBuilder) Sensitive added in v0.3.0

func (b *FieldBuilder) Sensitive() *FieldBuilder

func (*FieldBuilder) Type added in v0.3.0

func (b *FieldBuilder) Type(value string) *FieldBuilder

func (*FieldBuilder) Validator added in v0.3.0

func (b *FieldBuilder) Validator(value func(fieldValue any) error) *FieldBuilder

type FieldCondition

type FieldCondition struct {
	Condition   func(fieldValue any) (bool, error)
	FieldSetKey string
	FieldKey    string
}

func (*FieldCondition) Clone

func (c *FieldCondition) Clone() LoadCondition

func (*FieldCondition) FieldDependency

func (c *FieldCondition) FieldDependency() (fieldSetKey, fieldKey string)

func (*FieldCondition) Load

func (c *FieldCondition) Load(value any) (bool, error)

func (*FieldCondition) Validate

func (c *FieldCondition) Validate() []error

type FieldConditionBuilder added in v0.3.0

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

func FCB added in v0.3.0

func FCB() *FieldConditionBuilder

func NewFieldConditionBuilder added in v0.3.0

func NewFieldConditionBuilder() *FieldConditionBuilder

func (*FieldConditionBuilder) Condition added in v0.3.0

func (b *FieldConditionBuilder) Condition(value func(fieldValue any) (bool, error)) *FieldConditionBuilder

func (*FieldConditionBuilder) Create added in v0.3.0

func (*FieldConditionBuilder) FieldKey added in v0.3.0

func (*FieldConditionBuilder) FieldSetKey added in v0.3.0

func (b *FieldConditionBuilder) FieldSetKey(value string) *FieldConditionBuilder

type FieldSet

type FieldSet struct {
	Key            string
	LoadConditions LoadConditions
	Fields         Fields
	// contains filtered or unexported fields
}

func (*FieldSet) Clone

func (f *FieldSet) Clone() *FieldSet

type FieldSetBuilder added in v0.3.0

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

func FSB added in v0.3.0

func FSB() *FieldSetBuilder

func NewFieldSetBuilder added in v0.3.0

func NewFieldSetBuilder() *FieldSetBuilder

func (*FieldSetBuilder) Create added in v0.3.0

func (b *FieldSetBuilder) Create() *FieldSet

func (*FieldSetBuilder) Fields added in v0.3.0

func (b *FieldSetBuilder) Fields(value ...*Field) *FieldSetBuilder

func (*FieldSetBuilder) Key added in v0.3.0

func (b *FieldSetBuilder) Key(value string) *FieldSetBuilder

func (*FieldSetBuilder) LoadConditions added in v0.3.0

func (b *FieldSetBuilder) LoadConditions(value ...LoadCondition) *FieldSetBuilder

type FieldSets added in v0.2.0

type FieldSets []*FieldSet

type Fields added in v0.2.0

type Fields []*Field

Fields is a slice of Field elements providing context for configuration values

type FlagLoader

type FlagLoader struct {
	KeyPrefix      string
	OverrideLookup []string
}

func NewFlagLoader added in v0.3.0

func NewFlagLoader() *FlagLoader

func NewFlagLoaderWithKeyPrefix added in v0.3.0

func NewFlagLoaderWithKeyPrefix(keyPrefix string) *FlagLoader

func (*FlagLoader) Clone

func (l *FlagLoader) Clone() *FlagLoader

func (*FlagLoader) CloneLoader added in v0.4.0

func (l *FlagLoader) CloneLoader() Loader

func (*FlagLoader) Get

func (l *FlagLoader) Get(fieldSetKey, fieldKey string) (string, bool)

func (*FlagLoader) GetMap added in v0.4.0

func (l *FlagLoader) GetMap(fieldSetKey string, fieldKeys []string) map[string]string

func (*FlagLoader) HelpString

func (l *FlagLoader) HelpString(fieldSetKey, fieldKey string) string

func (*FlagLoader) Name

func (l *FlagLoader) Name() string

type JSONFileLoader added in v0.4.0

type JSONFileLoader struct {
	Decoder   JSONUnmarshal
	FilePaths []string
}

func NewJSONFileLoader added in v0.4.0

func NewJSONFileLoader() *JSONFileLoader

func NewJSONFileLoaderWithAttributes added in v0.4.0

func NewJSONFileLoaderWithAttributes(decoder JSONUnmarshal, filePaths ...string) *JSONFileLoader

func (*JSONFileLoader) Clone added in v0.4.0

func (l *JSONFileLoader) Clone() *JSONFileLoader

func (*JSONFileLoader) CloneLoader added in v0.4.0

func (l *JSONFileLoader) CloneLoader() Loader

func (*JSONFileLoader) Get added in v0.4.0

func (l *JSONFileLoader) Get(fieldSetKey, fieldKey string) (string, bool)

func (*JSONFileLoader) GetMap added in v0.4.0

func (l *JSONFileLoader) GetMap(fieldSetKey string, fieldKeys []string) map[string]string

func (*JSONFileLoader) HelpString added in v0.4.0

func (l *JSONFileLoader) HelpString(fieldSetKey, fieldKey string) string

func (*JSONFileLoader) Name added in v0.4.0

func (l *JSONFileLoader) Name() string

type JSONUnmarshal added in v0.4.0

type JSONUnmarshal func(data []byte, v interface{}) error

type LoadCondition

type LoadCondition interface {
	Clone() LoadCondition
	FieldDependency() (fieldSetKey string, fieldKey string)
	Load(value any) (bool, error)
	Validate() []error
}

type LoadConditions added in v0.2.1

type LoadConditions []LoadCondition

type Loader

type Loader interface {
	CloneLoader() Loader
	Name() string
	Get(fieldSetKey, fieldKey string) (value string, found bool)
	GetMap(fieldSetKey string, fieldKeys []string) (fieldValues map[string]string)
	HelpString(fieldSetKey, fieldKey string) string
}

type LoaderKeyOverride

type LoaderKeyOverride struct {
	LoaderName     string
	KeyOverride    string
	IgnorePrefixes bool
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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