Skip to content

An easy and extensible config framework with fluent interface based on Viper.

License

Notifications You must be signed in to change notification settings

tkrop/go-config

Repository files navigation

Config Framework

Build Coverage Coverage Quality Report FOSSA License Docs

Introduction

Goal of go-config is to provide an easy to use and extensible config framework with fluent interface based on Viper for services, jobs, and commands. It is supporting simple default-tags and prototype config to set up and change the reader defaults quickly.

How to start

In go-config you simply create your config as an extension of the config provided in this package as a base line as follows:

// Import for config prototype and config reader.
import "github.com/tkrop/go-config/config"

// Config root element for configuration.
type Config struct {
    config.Config `mapstructure:",squash"`

    Int int       `default:"31"`
    String string `default:"my-value"`
    Dur Duration  `default:"1m"`

    Service *my.ServiceConfig
}

Note: go-config makes it very simple to reuse the simple config structs provided by other libraries and components, since you can easily create any hierarchy of structs, slices, and even map[string]s containing native types, based on int, float, byte, rune, complex, and string. You can also use time.Time and time.Duration. However, you need to add the tag mapstructure:",squash", if you want to extend a config. If you do not flatten access via this tag, the inherited structured creates a sub-structure named config.

As usual in Viper, you can create your config using the reader that allows creating multiple configs while applying the setup mechanisms for defaults using the following convenience functions:

    reader := config.New[config.Config]("<prefix>", "<app-name>").
        SetDefaults(func(c *config.ConfigReader[config.Config]{
            c.SetDefault("int", 32)
        }).ReadConfig("main")

    config := reader.GetConfig("main")

This creates a standard config reader with defaults from the given config prototype reading in additional defaults from the <app-name>[-env].yaml-file and environment variables.

The defaults provided by the different options are overwriting each other in the following order:

  1. First, the values provided via the default-tags are applied.
  2. Second the values provided by the config prototype instance are applied.
  3. Third the values provided by Viper custom setup calls are applied. This also includes the convenient methods provided in this package.
  4. Forth the values provided in the <app-name>[-env].yaml-file are applied.
  5. And finally the values provided via environment variables are applied taking the highest precedence.

Note: While yo declare the reader with a default config structure, it is still possible to customize the reader arbitrarily, e.g. with flag support, and setup any other config structure by using the original Viper interface functions.

A special feature provided by go-config is to set up defaults using a partial or complete config prototype. While in the New constructor automatically an empty prototype is constructed and parsed for default-tags, you can use th SetDefaultConfig method to provide any pre-filled (sub-)config to updated and extend default values.

    reader := config.New("<prefix>", "<app-name>")).
        SetDefaultConfig("", &config.Config{
            Env: "prod",
        }, false).
        SetDefaultConfig("log", &log.Config{
            Level: "debug",
        }, false)

Logger setup

The go-config framework supports to set up a Logger in zerolog and logrus using the provided standard options out-of-the-box as follows:

    logger := config.Log.Setup[Rus|Zero](writer[, logger])

If no logger is provided, the standard logger is configured and returned.

Note: While the config supports zerolog, there is currently no real benefit of using it aside of its having a modern interface. Performance wise, the necessary transformations for pretty printing logs are a heavy burden that likely eats up all performance advantages compared to logrus.

Build info

Finally, go-config in conjunction with go-make supports a build information to track and access the origin of a command, service or job. While the build information is also auto-discovered, a full go-make integration provides the following variables in the main.go-file.

// Build information variables set by `go-make`.
var (
    // Path contains the package path (set by `go-make`).
    Path string
    // Version contains the custom version (set by `go-make`).
    Version string
    // Build contains the custom build time (set by `go-make`).
    Build string
    // Revision contains the custom revision (set by `go-make`).
    Revision string
    // Commit contains the custom commit time (set by `go-make`).
    Commit string
    // Dirty contains the custom dirty flag (set by `go-make`).
    Dirty string // Bool not supported by ldflags `-X`.
)

You can now use this information to set up the default build information in the config reader by using SetInfo during creation as follows:

func main() {
    reader := config.New("<prefix>", "<app-name>", &Config{}).
        SetInfo(info.New(Path, Version, Build, Revision, Commit, Dirty)).
}

If you don't want to use go-make, you can provide the variable defaults in the -ldflags="-X main.Path=... -X main.Version=... ... manually during your build.

Building

This project is using go-make, which provides default targets for most common tasks, to initialize, build, test, and run the software of this project. Read the go-make manual for more information about targets and configuration options.

The Makefile depends on a preinstalled go for version management, and makes heavy use of GNU tools, i.e. coretils, findutils, '(g)make', (g)awk, (g)sed, and not the least bash. For certain non-core-features it also requires docker/podman and curl. On MacOS, it uses brew to ensure that the latest versions with the exception docker/podman are.

Not: go-make automatically installs pre-commit and commit-msg hooks overwriting and deleting pre-existing hooks (see also Customizing Git - Git Hooks). The pre-commit hook calls make commit as an alias for executing test-go, test-unit, lint-<level>, and lint-markdown to enforce successful testing and linting. The commit-msg hook calls make git-verify message for validating whether the commit message is following the conventional commit best practice.

Terms of Usage

This software is open source under the MIT license. You can use it without restrictions and liabilities. Please give it a star, so that I know. If the project has more than 25 Stars, I will introduce semantic versions v1.

Contributing

If you like to contribute, please create an issue and/or pull request with a proper description of your proposal or contribution. I will review it and provide feedback on it as fast as possible.

About

An easy and extensible config framework with fluent interface based on Viper.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published