Skip to content

Latest commit

 

History

History
104 lines (80 loc) · 3.44 KB

README.md

File metadata and controls

104 lines (80 loc) · 3.44 KB

config

A utility for loading configuration YAML files.

Design

The config package provides a way to load configuration from YAML files. By default it will load a file named app-config.yaml in the root directory where your main package resides. Then it will load a file named app-config.<env>.yaml and use those values to override the previous configuration. The environment is determined by the value of the APP_ENV environment variable. If APP_ENV is not set, it will fall back to the value of the DEPLOYEMENT_STAGE environment variable. If neither are set, no override file will be loaded. The config package also supports loading configuration from a custom directory.

Custom Configuration Directory

There are two ways to specify a custom directory to load configuration files from. The first is to set the CONFIG_YAML_DIRECTORY environment variable. The second is to use the WithConfigYamlDir option when calling LoadConfiguration.

Configuration Structs

The config package uses the mapstructure package to load configuration into a struct. This allows for the use of tags to map configuration fields to struct fields. For example, the following YAML file:

auth:
  enable: true

can be loaded into the following struct:

type AuthConfiguration struct {
  Enable    *bool  `mapstructure:"enable"`
}

type Configuration struct {
  Auth     AuthConfiguration     `mapstructure:"auth"`
}

Environment Variables

The config package supports templated injection of environment variables in your YAML files to avoid storing sensitive values in the YAMLs. For example, the following YAML file:

database:
  driver: postgres
  data_source_name: host={{.PLATFORM_DATABASE_HOST}} user={{.PLATFORM_DATABASE_USER}} password={{.PLATFORM_DATABASE_PASSWORD}} port={{.PLATFORM_DATABASE_PORT}} dbname={{.PLATFORM_DATABASE_NAME}}

will have the environment variables PLATFORM_DATABASE_HOST, PLATFORM_DATABASE_USER, PLATFORM_DATABASE_PASSWORD, PLATFORM_DATABASE_PORT, and PLATFORM_DATABASE_NAME injected into the data_source_name field prior to loading into your configuration struct.

Usage

Example:

package main

import (
  "fmt"

  "github.com/chanzuckerberg/go-misc/config"
)

type AuthConfiguration struct {
	Enable    *bool  `mapstructure:"enable"`
}

type ApiConfiguration struct {
	Port                uint   `mapstructure:"port"`
	LogLevel            string `mapstructure:"log_level"`
}

type DBDriver string

func (d *DBDriver) String() string {
	return string(*d)
}

const (
	Sqlite   DBDriver = "sqlite"
	Postgres DBDriver = "postgres"
)

type DatabaseConfiguration struct {
	Driver         DBDriver `mapstructure:"driver"`
	DataSourceName string   `mapstructure:"data_source_name"`
}

type Configuration struct {
	Auth     AuthConfiguration     `mapstructure:"auth"`
	Api      ApiConfiguration      `mapstructure:"api"`
	Database DatabaseConfiguration `mapstructure:"database"`
}

func main() {
  cfg := &Configuration{}

  configOpts := []config.ConfigOption[Configuration]{
    config.WithConfigYamlDir[Configuration]("./configs"),
    config.WithConfigEditorFn(func(cfg *Configuration) error {
      // default to having auth enabled
      if cfg.Auth.Enable == nil {
        enable := true
        cfg.Auth.Enable = &enable
      }
      return nil
    }),
  }

  err := config.LoadConfiguration(cfg, configOpts...)
  if err != nil {
    panic(fmt.Sprintf("Failed to load app configuration: %s", err.Error()))
  }
}