Categorygithub.com/sahib/config
modulepackage
0.2.0
Repository: https://github.com/sahib/config.git
Documentation: pkg.go.dev

# README

.. BADGES:

.. API documentation: .. image:: https://godoc.org/github.com/sahib/config?status.svg :target: https://godoc.org/github.com/sahib/config

.. Test status via Travis: .. image:: https://img.shields.io/travis/sahib/config/master.svg?style=flat :target: https://travis-ci.org/sahib/config

.. Issue tracker: .. image:: https://img.shields.io/github/issues/sahib/config.svg?style=flat :target: https://github.com/sahib/config/issues

.. Release overview: .. image:: https://img.shields.io/github/release/sahib/config.svg?style=flat :target: https://github.com/sahib/config/releases

.. Download count: .. image:: https://img.shields.io/github/downloads/sahib/config/latest/total.svg :target: https://github.com/sahib/config/releases/latest

.. GPL tag: .. image:: http://img.shields.io/badge/license-GPLv3-4AC51C.svg?style=flat :target: https://www.gnu.org/licenses/quick-guide-gplv3.html.en

config

Go package for loading typesafe program configuration with validation and migration.

Motivation

There are already multiple packages available for loading application configuration from a file. Viper_ is just one of the more popular ones. They mostly focus on being convinient to use and to support lots of different configuration sources (environment variables, network sources, files...). I found none though that focuses on loading configuration and validating the incoming values. Some libraries supported default values, but all of them allowed »free form« configs -- i.e. defining keys that are not available in the program without any warning. This usually leads to a lot of user errors, wrong configuration and/or misbehaving programs. Config libraries should be very strict in what they accept and allow for validation and migration of the config when the layout changes -- this is why I wrote this package. If you didn't notice yet: The design is somewhat opinionated and not tailored to cover all possible use cases or to be overly convinient in exchange for power.

Note: The API is influenced by Python's ConfigObj_, but specs.cfg is part of the program.

.. _Viper: https://github.com/spf13/viper .. _ConfigObj: http://configobj.readthedocs.io/en/latest/configobj.html

Features

Validated: All configuration keys that are possible to set are defined with a normal type in a .go file as part of your application. Those values are also taken over as defaults, if they are not explicitly overwritten. This ensures that the program always have sane values to work with. Every key can be associated with a validation func, which can be used to implement further validation (i.e. allow a string key to only have certain enumeration values).

Versioned: Every config starts with a version of zero. If the application owning the config needs to change the layout, it can register a migration function to do this once an old configuration is loaded. This frees you from worrying about breaking changes in the config.

Typesafety: There is no stringification of values or other surprises (like in ConfigObj). Every configuration key has exactly one value, directly defined in Go's type system.

Change Notification and instant reloading: The application can reload the configuration anytime and also register a func that will be called when a certain key changes. This allows longer running daemon processes to react instantly on config changes, if possible.

Built-in Documentation: You can write down documentation for your configuration as part of the defaults definition, including a hint if this key needs a restart of the application to take effect.

Support for multiple formats: Only YAML is supported by default, since it suffices in the vast majority of use cases. If you need to, you can define your own Encoder and Decoder to fit your specific usecase.

Support for sub-sections: Sub-sections of a config can be used like a regular config object. Tip: Define your configuration hierarchy like the package structure of your program. That way you can pass sub-section config to those packages and you can be sure that they can only change the keys they are responsible for.

Support for placeholder sections: By using the special section name __many__ you can have several sections that all follow the same layout, but are allowed to be named differently.

Native support for slices: All native types (string, int, float and bool) can be packed into lists and easily set and accessed with special API for them.

Merging: Several configs from several sources can be merged. This might be useful e.g. when there are certain global defaults, that are overwritten with local defaults which are again merged with user defined settings.

Reset to defaults: Any part of the config can be reset to defaults at any time.

Migrations

Sometimes, you have to change the layout of your config or change default values. Since the configs of this module have version tags, this becomes quite simple. All you have to do is to write a migration function that transforms the old config to a new config - Take a look at the example below.

When do you have to migrate?

  • When moving or removing a key or section.
  • When changing the default of an existing key.
  • When adding new keys that are pre-filled by some user-defined logic.

If you're only adding new keys with constant defaults, there is no need for a migration.

Examples

  • Basic example._

.. _Basic example.: https://github.com/sahib/config/blob/master/example_test.go#L51

  • Migration example._

.. _Migration example.: https://github.com/sahib/config/blob/master/example_test.go#L127

If the validation was not succesful, you can either error out directly or continue with defaults as fallback:

.. code-block:: go

// Somewhere in your init code:
cfg, err := config.Open(config.NewYamlDecoder(fd), defaults)
if err != nil {
    log.Errorf("Failed to user config. Continuing with defaults.")
    cfg, err = config.Open(nil, defaults)
    if err != nil {
        // Something is really wrong. The defaults are probably wrong.
        // This is a programmer error and should be catched early though.
        log.Fatalf("Failed to load default config: %v", err)
    }
}

LICENSE

config is licensed under the conditions of the GPLv3 <https://www.gnu.org/licenses/quick-guide-gplv3.html.en>_. See the COPYING. distributed along the source for details.

Author

Christopher <sahib_> Pahl 2018

.. _sahib: https://www.github.com/sahib


Originally developed as part of »brig« (https://github.com/sahib/brig)

# Functions

DurationValidator asserts that the config value is a valid duration that can be parsed by time.ParseDuration.
EnumValidator checks if the supplied string value is in the `options` list.
FloatRangeValidator checks if the supplied float value lies in the inclusive boundaries of `min` and `max`.
FromYamlFile creates a new config from the YAML file located at `path`.
IntRangeValidator checks if the supplied integer value lies in the inclusive boundaries of `min` and `max`.
ListValidator takes any other validator and applies it to a list value.
MigrateKeys is a helper function to write migrations easily.
NewMigrater returns a new Migrater.
NewYamlDecoder creates a new Decoder that parses the data in `r`.
NewYamlEncoder creates a new Encoder that writes a YAML file with the config data.
Open creates a new config from the data in `r`.
ToYamlFile saves `cfg` as YAML at a file located at `path`.

# Constants

StrictnessIgnore silently ignores any programmer error.
StrictnessPanic will panic when a programmer error was made.
StrictnessWarn will log a complaint via log.Println().

# Variables

ErrNotVersioned is returned by Migrate() when it can't find the version tag.

# Structs

Config is a helper that is built around a representation defined by a Encoder/Decoder.
DefaultEntry represents the metadata for a default value in the config.
Migrater is a factory for creating version'd configs.

# Interfaces

Decoder defines how a byte stream can be parsed to a config.
Encoder defines how the config can be serialized to a byte stream.

# Type aliases

DefaultMapping is a container to hold all required DefaultEntries.
Migration is a function that is executed to replicate the changes between to versions.
Strictness defines how the API of the config reacts when it thinks that the programmer made a mistake.
Version specifies the version of the config.