cfx

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Mar 14, 2020 License: GPL-3.0 Imports: 13 Imported by: 0

README

cfx

An Fx (go.uber.org/fx) configuration provider that allows other components to load their configs in a standard way.

Usage

go get github.com/gen0cide/cfx

Since this is an Fx module, all you need to do is include the cfx Module in your Fx options:

app := fx.Option(
  // FOO here is the provided ENV VAR prefix. All cfx assignable env vars
  // will now be prefixed with "FOO_" when they're queried. I.e. CFX_ENVIRONMENT => FOO_ENVIRONMENT.
  cfx.NewFXEnvContext("FOO"),
  cfx.Module,
)

After that, the two main types you can use in your constructors are:

  • cfx.EnvContext - has information about the current environment and parses environment variables.
  • cfx.Container - Contains the merged and set YAML configuration objects. This allows you to quickly extract yaml sections dedicated to individual components.

How does it work

That's easy. cfx does two things to load configuration:

  1. determines the config directory
  2. loads configuration files

Lets take those one at a time.

Determining the config directory

The first thing cfx does is gathers information about the environment it's operating in. It does this through a combination of system info (hostname, GOOS, etc.) as well as environment variables.

This results in the cfx.EnvContext type doc.

Two properties of the cfx.EnvContext are the AppPath and ConfigPath. These are locations on the filesystem. By DEFAULT, AppPath will be the current working directory when the program is run. ConfigPath will simply be AppPath + "config". So if the app is running out of /opt/foo, the ConfigPath will be set to /opt/foo/config.

cfx checks to make sure that both of these paths are valid folders and that the application has permissions to read them.

You can customize AppPath with the environment variable CFX_APP_DIR and if you wish to completely override ConfigPath you can use the environment variable CFX_CONFIG_DIR.

Lastly, cfx expects an Environment to be defined. By default, that is set to development, but can be overridden with the CFX_ENVIRONMENT environment variable. (Or more precisely, ${ENV_PREFIX}_ENVIRONMENT where ENV_PREFIX is the value that you passed into the cfx.NewFXEnvContext() constructor.) Env Prefixes can be uppercase alpha numeric and include '_' characters, but it cannot start nor end with one.

Users can define their own environments, but they must conform to the following rules:

  1. lowercase alpha numeric characters only
  2. longer than 2 characters
  3. shorter than 64 characters

The exported function cfx.ParseEnv is what performs this validation.

Loading the Configuration File

So at this point, we've determined that theres a folder for configurations and an environment ID. You can now place your YAML configurations inside ${environment}.yaml inside the config directory. cfx will attempt to load at most 2 configurations - base.yaml and ${environment}.yaml.

These configurations are loaded in order, and any changes found in ${environment}.yaml are merged on top of the base.yaml. This makes defining base configurations easy, with overrides for individual environments.

cfx doesn't care if there is no base.yaml, but will load it if it is present.

Populating your configuration structs

Lets say your YAML looks like this:

foo:
  name: bob

bar:
  location: gym

Using struct tags, you can define go types that corraspond to the structure under the top level YAML keys.

type Foo struct {
  Name string `yaml:"name"`
}

type Bar struct {
  Location string `yaml:"gym"`
}

To unmarshal the configuration out of a cfx.Container, simply do this:


_ = cfg
// assume that cfg is the `cfx.Container` type

f := Foo{}
b := Bar{}

err := cfg.Populate("foo", &f)
if err != nil {
  // handle error
}

err := cfg.Populate("bar", &b)
if err != nil {
  // handle error
}

// f.Name now equals "bob"
// b.Location now equals "gym"

Those are easily setup in your fx constructors. Take a look at the example repo here. It reproduces this exact example with a full main.

Documentation

Index

Constants

View Source
const (
	// Version defines the current version of the cfx package.
	Version = "0.0.4"
)

Variables

View Source
var (
	// ErrNoConfigsLoaded is thrown when no configuration files were loaded.
	ErrNoConfigsLoaded = errors.New("no configuration files were loaded into the container")

	// ErrConfigNotFound is thrown when a configuration cannot be located
	ErrConfigNotFound = errors.New("could not find any valid config files")
)
View Source
var Module = fx.Provide(
	NewConfig,
)

Module is the Fx provider that gives access to cfgfx.EnvContext and cfgfx.Container types. Note: You should use cfx.NewFXEnvContext("PREFIX") to populate a constructor for EnvContext types. If you wish to leave "PREFIX" empty, you can - the default prefix is "CFX".

Functions

func NewFXEnvContext added in v0.0.4

func NewFXEnvContext(prefix string) fx.Option

NewFXEnvContext is used to create a constructor for cfx applications to self configure with an optional prefix.

Types

type Container

type Container interface {
	// Populate is used to load a block of YAML configuration into
	// a target struct. Target should be a pointer to the config struct value.
	Populate(key string, target interface{}) error
}

Container is the type that allows users to parse sections of the YAML configuration as a coherent configuration tree.

func NewConfig

func NewConfig(env EnvContext) (Container, error)

NewConfig is used to create a container that can be used to extract configuration elements from a YAML file.

type DeploymentContext added in v0.0.4

type DeploymentContext struct {
	// AppID is a specific identifier for the application.
	AppID string `json:"app_id,omitempty" yaml:"app_id,omitempty" mapstructure:"app_id,omitempty"`

	// ServiceID is a specific identifier that can be used to group several related apps together.
	ServiceID string `json:"service_id,omitempty" yaml:"service_id,omitempty" mapstructure:"service_id,omitempty"`

	// InstanceID should be the unique instance identifier (blank, otherwise populated from cloud metadata)
	InstanceID string `json:"instance_id,omitempty" yaml:"instance_id,omitempty" mapstructure:"instance_id,omitempty"`

	// Region can be used to specify the regional location of the environment.
	Region string `json:"region,omitempty" yaml:"region,omitempty" mapstructure:"region,omitempty"`

	// AvailabilityZone can be used to specify the zone within the region.
	AvailabilityZone string `json:"availability_zone,omitempty" yaml:"availability_zone,omitempty" mapstructure:"availability_zone,omitempty"`

	// NetworkID is a generic identifier to help classify an environment's network.
	NetworkID string `json:"network_id,omitempty" yaml:"network_id,omitempty" mapstructure:"network_id,omitempty"`

	// DatacenterID is a generic identifier to help classify an environment's datacenter.
	DatacenterID string `json:"datacenter_id,omitempty" yaml:"datacenter_id,omitempty" mapstructure:"datacenter_id,omitempty"`
}

DeploymentContext holds information about the current deployment environment of the application.

type EnvContext

type EnvContext struct {
	// Environment is the primary identifier about what the environment we're running in.
	Environment EnvID `json:"environment,omitempty" yaml:"environment,omitempty" mapstructure:"environment,omitempty"`

	// The prefix of the applications environment variables
	EnvPrefix EnvKeyPrefix `json:"env_prefix,omitempty" yaml:"env_prefix,omitempty" mapstructure:"env_prefix,omitempty"`

	// AppPath is the directory that the app can consider it's base working directory.
	// If no value is defined in an ENV_VAR, the app will use the current working directory
	// of the running binary.
	AppPath string `json:"app_path,omitempty" yaml:"app_path,omitempty" mapstructure:"app_path,omitempty"`

	// ConfigPath is the directory where configuration files and data might be located.
	ConfigPath string `json:"config_path,omitempty" yaml:"config_path,omitempty" mapstructure:"config_path,omitempty"`

	// Host holds information about the underlying host.
	Host HostContext `json:"host,omitempty" yaml:"host,omitempty" mapstructure:"host,omitempty"`

	// Go holds information about the os and architecture of the machine, as well as the version of the runtime.
	Go GoContext `json:"go,omitempty" yaml:"go,omitempty" mapstructure:"go,omitempty"`

	// Deployment holds information about the deployment of the application.
	Deployment DeploymentContext `json:"deployment,omitempty" yaml:"deployment,omitempty" mapstructure:"deployment,omitempty"`

	// User holds information about the user the application is running as.
	User UserContext `json:"user,omitempty" yaml:"user,omitempty" mapstructure:"user,omitempty"`

	// Process holds information about the applications process (pid and ppid).
	Process ProcessContext `json:"process,omitempty" yaml:"process,omitempty" mapstructure:"process,omitempty"`
}

EnvContext is a type that holds information about the current running application, including several properties that can be configured via ENVIRONMENT VARIABLES. This is useful for environment aware applications to make decisions based upon where they might be executing.

func NewEnvContext

func NewEnvContext(prefix string) (EnvContext, error)

NewEnvContext creates a new, populated EnvContext, optionally returning an error if an error occurs during the population of the data.

type EnvID added in v0.0.3

type EnvID string

EnvID represents a specific environment identifier within the application.

func ParseEnv

func ParseEnv(v string) (EnvID, error)

ParseEnv is used to parse an environment string to determine if it's valid. Environment strings should be lowercase alphanumeric, between 2 and 64 characters in length. No special characters. If you attempt to pass an empty env to this function, it will return the default environment (development).

func (EnvID) String added in v0.0.3

func (e EnvID) String() string

String implements the fmt.Stringer interface.

type EnvKeyPrefix added in v0.0.4

type EnvKeyPrefix string

EnvKeyPrefix is a type that is used to uniquely prefix the environment variable settings.

func ParseEnvKeyPrefix added in v0.0.4

func ParseEnvKeyPrefix(v string) (EnvKeyPrefix, error)

ParseEnvKeyPrefix is used to determine if the user supplied environment variable key prefix is valid. For it to be valid, it must be an UPPERCASE alpha-numeric string greater than 2 in length, but less than 64. It can include a '_' character, but it cannot be the first or last character in the string.

type EnvResult

type EnvResult struct {
	fx.Out

	Environment EnvContext
}

EnvResult is used as an Fx container, wrapping the EnvContext output.

type EnvVar added in v0.0.4

type EnvVar string

EnvVar is a type alias to allow default environment variable names to be set, and dynamically calculated.

const (
	// KeyEnvironment is used to specify the environment that other Fx modules
	// can adjust to accordingly. These values are defined in the cfgfx.Env enum.
	KeyEnvironment EnvVar = EnvVar("ENVIRONMENT")

	// KeyAppPath is the ENV_VAR used to specify a custom application working directory.
	KeyAppPath EnvVar = EnvVar("APP_DIR")

	// KeyConfigPath is used to define the filesystem path where configuration
	// YAML files can be located.
	KeyConfigPath EnvVar = EnvVar("CONFIG_DIR")

	// KeyAppID is the ENV_VAR key used to populate a custom application identifier value.
	KeyAppID EnvVar = EnvVar("APP_ID")

	// KeyServiceID is the ENV_VAR key used to populate a custom service identifier value.
	KeyServiceID EnvVar = EnvVar("SERVICE_ID")

	// KeyInstanceID is used to populate an Instance ID into the EnvContext.
	// TODO: Autopopulate this value not from ENV_VAR, but from instance metadata.
	KeyInstanceID EnvVar = EnvVar("INSTANCE_ID")

	// KeyRegion is the ENV_VAR used to populate the Region field in the EnvContext.
	// TODO: Autopopulate this value not from ENV_VAR, but from instance metadata.
	KeyRegion EnvVar = EnvVar("REGION")

	// KeyAvailabilityZone is the ENV_VAR used to populate the AvailabilityZone field in the EnvContext.
	// TODO: Autopopulate this value not from ENV_VAR, but from instance metadata.
	KeyAvailabilityZone EnvVar = EnvVar("AVAILABILITY_ZONE")

	// KeyNetworkID the ENV_VAR used to specify a custom network ID.
	KeyNetworkID EnvVar = EnvVar("NETWORK_ID")

	// KeyDatacenterID is used to tag the environment with a datacenter specific identification.
	KeyDatacenterID EnvVar = EnvVar("DATACENTER_ID")

	// If the user doesn't specify an EnvKeyPrefix, this one will be used.
	DefaultEnvKeyPrefix = EnvKeyPrefix("CFX")

	DefaultEnvVarSeparator = `_`
)

Environment Variables that can be used to configure things.

func (EnvVar) Get added in v0.0.4

func (e EnvVar) Get(p EnvKeyPrefix) string

Get attempts to get the environment variable's value with the included EnvKeyPrefix.

func (EnvVar) Key added in v0.0.4

func (e EnvVar) Key(p EnvKeyPrefix) string

Key attempts to bond an EnvKeyPrefix onto the environment variable using an `_` character.

type GoContext added in v0.0.4

type GoContext struct {
	// OS is the operating system the machine is running as. (runtime.GOOS)
	OS string `json:"os,omitempty" yaml:"os,omitempty" mapstructure:"os,omitempty"`

	// Arch is the cpu architecture of the underlying machine. (runtime.GOARCH)
	Arch string `json:"arch,omitempty" yaml:"arch,omitempty" mapstructure:"arch,omitempty"`

	// Version is the version of Go that was used to compile the application. (runtime.Version())
	Version string `json:"version,omitempty" yaml:"version,omitempty" mapstructure:"version,omitempty"`
}

GoContext holds information about the Go environment of the running application.

type HostContext added in v0.0.4

type HostContext struct {
	// Hostname is the name of the machine running the code.
	Hostname string `json:"hostname,omitempty" yaml:"hostname,omitempty" mapstructure:"hostname,omitempty"`

	// UUID is a low level machine ID that is unique to the OS installation
	UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty" mapstructure:"uuid,omitempty"`

	// Timezone of the underlying operating system.
	Timezone string `json:"timezone,omitempty" yaml:"timezone,omitempty" mapstructure:"timezone,omitempty"`
}

HostContext holds information about the underlying host.

type ProcessContext added in v0.0.4

type ProcessContext struct {
	// PID is the process ID of the current application
	PID int `json:"pid,omitempty" yaml:"pid,omitempty" mapstructure:"pid,omitempty"`

	// PPID is the parent process ID of the current application
	PPID int `json:"ppid,omitempty" yaml:"ppid,omitempty" mapstructure:"ppid,omitempty"`
}

ProcessContext holds information about the current process.

type UserContext added in v0.0.4

type UserContext struct {
	// Username of the running process's user
	Username string `json:"username,omitempty" yaml:"username,omitempty" mapstructure:"username,omitempty"`

	// User ID of the running process
	UID string `json:"uid,omitempty" yaml:"uid,omitempty" mapstructure:"uid,omitempty"`

	// Group ID of the running process
	GID string `json:"gid,omitempty" yaml:"gid,omitempty" mapstructure:"gid,omitempty"`
}

UserContext holds information about the user the current process is running as.

Jump to

Keyboard shortcuts

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