config

package module
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2025 License: MIT Imports: 8 Imported by: 0

README

License

Library

This repo is licensed under the MIT license. Please read the full license here.

Config

This library allows you to read configuration data from a variety of sources. Information is automatically merged according to the priority of the source. Sources are (in priority order):

  • Commandline arguments
  • Environment variables
  • Yaml or Json configuration files

You can access configuration data by populating a struct or by direct access via function calls.

Configuration Files

You can specify a file to read from, but the following files will be examined by default. If you have more than one of the files below, they will be merged and can override each other according to priority. The default files are (in priority order):

    • env.local.json
    • env.local.yml
    • config.local.json
    • config.local.yml
    • env.json
    • env.yml
    • config.json
    • config.yml
    • config/config.json
    • config/config.yml
    • build/config.json
    • build/config.yml

Populating A Struct

You can read configuration data by populating a struct. You can make use of the following tags in your structs:

  • default - set a default value if no source data is found
  • required (true) - returns an error if no data is found for this variable
  • src - override the name of the data source - if you add src="myVar" to any variable, it will populate from the environment variable or yaml or json variable myVar
  • base64 - this will decode your sourced data from base64 encoding. Set this tag to
    • optional - in this case the data will be decoded from base64. If decoding fails, the original value will be used. This is useful for times when the source of the data changes and sometimes you need to base64 encode, and sometimes not
    • true - in this case the data will be decoded; if decoding fails the data will be set to the empty string

Commandline arguments, environment variables and variables in yaml or json files can be either a direct case match, or all in capitals eg. if your struct contains the variable Name, the environment variables Name and NAME will be a match. If you require an exact match, use the struct tag literal (set to true) to enforce an exact match.

Code Examples

Populating A Struct
package main

import (
    "encoding/json"
    "fmt"
    "github.com/driscollos/config"
    "time"
)

type Teacher struct {
    Name    string `required:"true"`
    Age     int
    Classes map[string]struct {
        Pupils []struct {
            Name       string
            Attendance float64
            Enrolled   bool `default:"true"`
        }
        ClassLength time.Duration
        Location    string `default:"Spare Classroom"`
    }
    LuckyNumbers []float64
    LotteryPicks []float64 `default:"10,31,55"`
}

func main() {
    t := Teacher{}
    c := config.New()
    c.Populate(&t)

    bytes, _ := json.Marshal(t)
    fmt.Println(string(bytes))
}

And the associated yaml file (in this case env.yml)

Name: John
Age: 41
Classes:
  Computer Science:
    ClassLength: 2 hours
    Pupils:
      - Name: Bob
        Attendance: 78.4
        Enrolled: yes
      - Name: Theresa
        Attendance: 81.6
        Enrolled: y
      - Name: Jim
        Attendance: 80.5
        Enrolled: true
      - Name: Tom
        Attendance: 30.2
        Enrolled: n
      - Name: Henry
        Attendance: 45.82
        Enrolled: false
      - Name: Laura
        Attendance: 88.1
  History:
    ClassLength: 3 hours
    Pupils:
      - Name: Pete
        Attendance: 81.4
        Enrolled: 1
    Location: Room C4
LuckyNumbers:
  - 10
  - 21
  - 56

The output of this code will be:

{"Name":"John","Age":41,"Classes":{"Computer Science":{"Pupils":[{"Name":"Bob","Attendance":78.4,"Enrolled":true},{"Name":"Theresa","Attendance":81.6,"Enrolled":true},{"Name":"Jim","Attendance":80.5,"Enrolled":true},{"Name":"Tom","Attendance":30.2,"Enrolled":false},{"Name":"Henry","Attendance":45.82,"Enrolled":false},{"Name":"Laura","Attendance":88.1,"Enrolled":true}],"ClassLength":7200000000000,"Location":"Spare Classroom"},"History":{"Pupils":[{"Name":"Pete","Attendance":81.4,"Enrolled":true}],"ClassLength":10800000000000,"Location":"Room C4"}},"LuckyNumbers":[10,21,56],"LotteryPicks":[10,31,55]}

You can override any of the data in the yaml file by setting an environment variable (as these have higher priorty than yaml files). For example running this:

export Classes_Computer_Science_Pupils_0_Name="Steve"

will change the output of the code to this:

{"Name":"John","Age":41,"Classes":{"Computer Science":{"Pupils":[{"Name":"Steve","Attendance":78.4,"Enrolled":true},{"Name":"Theresa","Attendance":81.6,"Enrolled":true},{"Name":"Jim","Attendance":80.5,"Enrolled":true},{"Name":"Tom","Attendance":30.2,"Enrolled":false},{"Name":"Henry","Attendance":45.82,"Enrolled":false},{"Name":"Laura","Attendance":88.1,"Enrolled":true}],"ClassLength":7200000000000,"Location":"Spare Classroom"},"History":{"Pupils":[{"Name":"Pete","Attendance":81.4,"Enrolled":true}],"ClassLength":10800000000000,"Location":"Room C4"}},"LuckyNumbers":[10,21,56],"LotteryPicks":[10,31,55]}

Note that:

  • You can populate elements of a slice by adding the integer index to your env variable
  • Spaces in the name of variables eg. the class Computer Science should be converted to underscores eg Computer_Science

Accessing Variables Directly

You can access parameters with the following type functions. Give the name of the variable you want to access; separate levels of nested fields with an underscore eg. Classes_Computer_Science_Pupils_0_Name.

These functions will take environment variables and provide them in various formats.

  • Bool(param string) bool
  • Date(param string) time.Time
  • Exists(name string) bool
  • Float(param string) float64
  • Int(param string) int
  • IntWithDefault(param string, defaultVal int) int
  • String(param string) string
  • StringWithDefault(param, defaultVal string) string

Duration Supported Formats

Parsing of time.Duration default values in struct tags supports a variety of conventions. All of the following are supported defaults:

  • 1s1m1h1d
  • 1s, 1m, 1h, 1d
  • 1 second, 1 minute, 1 hour, 1 day
  • 1 sec, 1 minute, 1 hr, 1d
  • 1 second, 1 min, 1hr, 1 day

Specifying a file to source data from

You can specify the exact file which should be used to populate your config. If you specify a source file, all other potential file sources will be ignored. Data from the terminal and the environment will still take precedence, in that order. Here is an example:

c := config.New()
c.Source("./env.yml")

Hot Reload

You can reload your configuration on the fly without stopping your service. In order to do this, you must make a change to any of the files which contained data when your service started up eg env.local.yml. If one of the options for a local data file did not exist when the service started, it will not be checked for changes.

Using Hot Reload

To activate hot reload, call the HotReload(ctx context.Context, actions ...interface{}) function. The first parameter is a context; if the context is ever done then the hot reload functionality will cease. This is very useful for handling os signals.

The second parameter is a variadic list of actions, which are defined as either a pointer to a struct or a function which has no parameters. You can supply any number of these in any order you like. If you pass a pointer to a struct it will be rehydrated from the latest information whenever the source data changes; it is the same as calling the Populate() function.

If you pass a function with no parameters, it will be called any time the source data changes; you can use this to update db connections or reload anything which relies upon the data in your configuration.

In the example below, we rehydrate the struct myInformation every time the underlying data changes, and we also call a function which makes use of a closure to act on the new data.

package main

import (
    "context"
    "fmt"
    "github.com/driscollos/config"
    "time"
)

func main() {
    c := config.New()
    myInformation := myData{}
    if err := c.Populate(&myInformation); err != nil {
        fmt.Println("error", err.Error())
    }
    c.HotReload(context.Background(), &myInformation, func() {
        func(data myData) {
            fmt.Println("new name is:", data.Name)
        }(myInformation)
    })

    for {
        fmt.Println("name", myInformation.Name)
        time.Sleep(time.Second)
    }
}

type myData struct {
    Name string
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config interface {

	// Bool will attempt to convert the parameter whose name matches the param argument into a boolean. The default
	// return value is FALSE
	Bool(param string) bool

	// Date will attempt to convert the parameter whose name matches the param argument into a time.Time value - if the
	// parameter is not known to the Config struct or there is an error with conversion this will be reflected in the
	// error return value
	Date(param, layout string) (time.Time, error)

	// Float will attempt to convert the parameter whose name matches the param argument into a float64 value. The default
	// return value is 0
	Float(param string) float64

	// HotReload will attempt to check for changes any files used as a source of information. If changes are detected, the
	// config library will reload the information from that file. Populate actions with a mix of structs to be repopulated
	// with the new information, and functions without parameters to be called in response to the change eg myfunc() {}
	HotReload(ctx context.Context, actions ...interface{})

	// Int will attempt to convert the parameter whose name matches the param argument into an int value. The default
	// return value is 0
	Int(param string) int

	// Populate will attempt to match the fields in the container (struct) argument to the parameters known to the Config
	// struct. It will populate as many fields as it can, coverting them to the correct types. If there are any errors during
	// population this will be reflected in the error return variable - this includes failing to populate fields which are marked
	// as required:"true" in struct tags
	Populate(container interface{}) error

	// Source explicitly specifies a file to be used as a source of information. Information from higher priority sources such as
	// the terminal or environment variables is still considered
	Source(path string)

	// String will attempt to convert the parameter whose name matches the param argument into a string value. The default
	// return value is ""
	String(param string) string
}

Config will parse terminal arguments, environment variables or configuration sourced from json or yaml files in order to understand the configuration of your application or service. This configuration can be retrieved either by calling the access methods (which will attempt to convert the requested value to their respective data type) or by passing a struct to Populate - which will populate the matching fields of your configuration struct.

func New

func New() Config

Directories

Path Synopsis
internal
mocks
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.

Jump to

Keyboard shortcuts

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