piper

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Nov 22, 2021 License: Apache-2.0 Imports: 7 Imported by: 0

README

piper - Simple Wrapper For Viper

example workflow Go Report Card

  • Single Source of Truth
  • Generated Key Structs, No Typo
  • Config Inheritance
  • Multiple Config Strategies Support
  • Cache For Better Performance

Why Piper

If you are familiar with Django, this is how Django settings module looks like:

└── settings
    ├── base.py
    ├── dev.py
    ├── stage.py
    └── prod.py

dev.py will inherit from base.py, also stage.py will inherit from dev.py.

Start Django with selected setting:

export DJANGO_SETTINGS_MODULE=mysite.settings.prod
django-admin runserver

And this is how you access settings in Django:

from django.conf import settings

author = settings.Author

I want to have similar experience with Viper, so here comes this wrapper:

└── config
    ├── base.toml
    ├── dev.toml
    ├── stage.toml
    └── prod.toml

This is how you access config using piper:

import "your_project/config"

func main() {
	piper.Load("config/stage.toml")
	author = piper.GetString(config.Author)
}

Check example folder for more details.

Installation

go get github.com/Yiling-J/piper/cmd

Add Config Files

Add your config files to your config folder, usually your config folder should be under project root folder.

toml example, you can also use yaml or json.

project
└── config
    ├── base.toml
    ├── dev.toml
    ├── stage.toml
    └── prod.toml

To support inheritance, you need to add a special key to your config file called pp_imports

pp_imports = ["base.toml", "dev.toml"]

Piper will resolve that automatically. Also order matters here, this line means import base.toml first, then import dev.toml and merge. After all pp_imports merged, import current file.

Config Key Generation

Run code generation from the root directory of the project as follows:

go run github.com/Yiling-J/piper/cmd your_config_folder

In this step piper will load all files in your config folder and merge them together. Then piper will generate config.go under your config folder, include all your config keys. Also you will see the config structure when piper generating code.

After code genertation, your config folder should look like:

└── config
    ├── base.toml
    ├── dev.toml
    ├── stage.toml
    ├── prod.toml
    └── config.go

Use Piper

Strategy I - Embed

embed your config folder into your code, single executable when you deploy.

import (
	"github.com/Yiling-J/piper"
	"your_project/example/config"
)

//go:embed config/*
var configFS embed.FS

piper.SetFS(configFS)
piper.Load("config/stage.toml")
author := piper.GetString(config.Author)
Strategy II - Embed with Env

embed your config folder into your code, single executable when you deploy, and replace secret with env.

import (
	"github.com/Yiling-J/piper"
	"your_project/example/config"
)

//go:embed config/*
var configFS embed.FS

os.Setenv("SECRET", "qux")
piper.SetFS(configFS)
// make sure turn on AutomaticEnv first then loading config,
// this way the env vaiable is also cached, so IGet* methods can work properly
piper.V().AutomaticEnv()
piper.Load("config/stage.toml")
secret := piper.GetString(config.Secret)

Strategy III - Copy config directory

copy config folder when building docker image, so the true config folder exists.

import (
	"github.com/Yiling-J/piper"
	"your_project/example/config"
)

piper.Load("config/stage.toml")
author := piper.GetString(config.Author)
Strategy IV - Mix embed and copy directory

Embed your config folder, but keep some secret keys in a real config file.

import (
	"github.com/Yiling-J/piper"
	"your_project/example/config"
)

//go:embed config/*
var configFS embed.FS

// "config/stage_with_secret.toml" is not in source code,
// may come from docker build or k8s ConfigMap
piper.SetFS(configFS)
piper.Load("config/stage_with_secret.toml")
author := piper.GetString(config.Author)

True directory will take precedence over embeded directory.

Access Viper

Piper is just a wrapper, so you can always get the wrapped viper instance:

v := piper.V()

Be careful when using viper directly to load config, piper may not work properly.

Piper or Pipers?

Piper comes ready to use out of the box. There is no configuration or initialization needed to begin using Piper. Since most applications will want to use a single central repository for their configuration, the piper package provides this. It is similar to a singleton.

In all of the examples above, they demonstrate using piper in its singleton style approach.

Working with multiple pipers

You can also create many different pipers for use in your application. Each will have its own unique set of configurations and values. Each can read from a different config file. All of the functions that piper package supports are mirrored as methods on a piper.

Example:

x := piper.New()
y := piper.New()

x.Load("config/x.toml")
y.Load("config/y.toml")

// access viper
// p := x.V

When working with multiple pipers, it is up to the user to keep track of the different pipers.

Performance

Sometimes you may find viper a little slow, because viper need to check in the following order on Get: flag, env, config file, key/value store. If you have confidence some of your configs won't change, you can use piper's IGet* methods. Piper will build a configs cache on Load, and those IGet* methods will get config from cache directly.

goos: darwin
goarch: amd64
pkg: github.com/Yiling-J/piper/integration
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz

BenchmarkGet-12           895533              1278 ns/op
BenchmarkIGet-12        19141876                61.35 ns/op

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Get

func Get(key string) interface{}

func GetBool

func GetBool(key string) bool

func GetDuration

func GetDuration(key string) time.Duration

func GetFloat64

func GetFloat64(key string) float64

func GetInt

func GetInt(key string) int

func GetInt32

func GetInt32(key string) int32

func GetInt64

func GetInt64(key string) int64

func GetIntSlice

func GetIntSlice(key string) []int

func GetString

func GetString(key string) string

func GetStringSlice

func GetStringSlice(key string) []string

func GetUint

func GetUint(key string) uint

func GetUint32

func GetUint32(key string) uint32

func GetUint64

func GetUint64(key string) uint64

func IGet

func IGet(key string) interface{}

func IGetBool

func IGetBool(key string) bool

func IGetDuration

func IGetDuration(key string) time.Duration

func IGetFloat64

func IGetFloat64(key string) float64

func IGetInt

func IGetInt(key string) int

func IGetInt32

func IGetInt32(key string) int32

func IGetInt64

func IGetInt64(key string) int64

func IGetIntSlice

func IGetIntSlice(key string) []int

func IGetString

func IGetString(key string) string

func IGetStringSlice

func IGetStringSlice(key string) []string

func IGetUint

func IGetUint(key string) uint

func IGetUint32

func IGetUint32(key string) uint32

func IGetUint64

func IGetUint64(key string) uint64

func Load

func Load(name string) error

func ReadInConfig

func ReadInConfig() error

func Reset

func Reset()

func SetFS

func SetFS(fs embed.FS)

func V

func V() *viper.Viper

Types

type Piper

type Piper struct {
	V *viper.Viper
	// contains filtered or unexported fields
}

func New

func New() *Piper

func (*Piper) Get

func (p *Piper) Get(key string) interface{}

func (*Piper) GetBool

func (p *Piper) GetBool(key string) bool

func (*Piper) GetDuration

func (p *Piper) GetDuration(key string) time.Duration

func (*Piper) GetFloat64

func (p *Piper) GetFloat64(key string) float64

func (*Piper) GetInt

func (p *Piper) GetInt(key string) int

func (*Piper) GetInt32

func (p *Piper) GetInt32(key string) int32

func (*Piper) GetInt64

func (p *Piper) GetInt64(key string) int64

func (*Piper) GetIntSlice

func (p *Piper) GetIntSlice(key string) []int

func (*Piper) GetString

func (p *Piper) GetString(key string) string

func (*Piper) GetStringSlice

func (p *Piper) GetStringSlice(key string) []string

func (*Piper) GetUint

func (p *Piper) GetUint(key string) uint

func (*Piper) GetUint32

func (p *Piper) GetUint32(key string) uint32

func (*Piper) GetUint64

func (p *Piper) GetUint64(key string) uint64

func (*Piper) IGet

func (p *Piper) IGet(key string) interface{}

IGet

func (*Piper) IGetBool

func (p *Piper) IGetBool(key string) bool

func (*Piper) IGetDuration

func (p *Piper) IGetDuration(key string) time.Duration

func (*Piper) IGetFloat64

func (p *Piper) IGetFloat64(key string) float64

func (*Piper) IGetInt

func (p *Piper) IGetInt(key string) int

func (*Piper) IGetInt32

func (p *Piper) IGetInt32(key string) int32

func (*Piper) IGetInt64

func (p *Piper) IGetInt64(key string) int64

func (*Piper) IGetIntSlice

func (p *Piper) IGetIntSlice(key string) []int

func (*Piper) IGetString

func (p *Piper) IGetString(key string) string

func (*Piper) IGetStringSlice

func (p *Piper) IGetStringSlice(key string) []string

func (*Piper) IGetUint

func (p *Piper) IGetUint(key string) uint

func (*Piper) IGetUint32

func (p *Piper) IGetUint32(key string) uint32

func (*Piper) IGetUint64

func (p *Piper) IGetUint64(key string) uint64

func (*Piper) Load

func (p *Piper) Load(name string) error

func (*Piper) MergeInConfig

func (p *Piper) MergeInConfig() error

func (*Piper) ReadInConfig

func (p *Piper) ReadInConfig() error

func (*Piper) SetFS

func (p *Piper) SetFS(fs embed.FS)

Directories

Path Synopsis
config
nolint
nolint
nolint
nolint
nolint

Jump to

Keyboard shortcuts

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