Giter VIP home page Giter VIP logo

uconfig's Introduction

uConfig GoDoc Build Status Go Report Card Coverage

Lightweight, zero-dependency, and extendable configuration management.

uConfig is extremely light and extendable configuration management library with zero dependencies. Every aspect of configuration is provided through a plugin, which means you can have any combination of flags, environment variables, defaults, secret providers, Kubernetes Downward API, and what you want, and only what you want, through plugins.

uConfig takes the config schema as a struct decorated with tags, nesting is supported.

Supports all basic types, time.Duration, and any other type through encoding.TextUnmarshaler interface. See the flat view package for details.

Example Configuration:

package database
// Config holds the database configurations.
type Config struct {
  Address  string `default:"localhost"`
  Port     string `default:"28015"`
  Database string `default:"my-project"`
}
package redis
// Config describes the requirement for redis client.
type Config struct {
  Address  string        `default:"redis-master"`
  Port     string        `default:"6379"`
  Password string        `secret:""`
  DB       int           `default:"0"`
  Expire   time.Duration `default:"5s"`
}
package main



import (
  "encoding/json"

  "github.com/omeid/uconfig"

  "$PROJECT/redis"
  "$PROJECT/database"
)

// Config is our application config.
type Config struct {

  // yes you can have slices.
  Hosts    []string `default:"localhost,localhost.local" usage:"the ip or domains to bind to"`

  Redis    redis.Config
  Database database.Config

}



func main() {

	conf := &Config{}

	files := uconfig.Files{
		{"config.json", json.Unmarshal, true},
		// you can of course add as many files
		// as you want, and they will be applied
		// in the given order.
	}

	_, err := uconfig.Classic(&conf, files)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// use conf as you please.
	// let's pretty print it as JSON for example:
	configAsJson, err := json.MarshalIndent(conf, "", " ")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Print(string(configAsJson))

}

Now lets run our program:

$ go run main.go -h

Supported Fields:
FIELD                FLAG                  ENV                  DEFAULT                      USAGE
-----                -----                 -----                -------                      -----
Hosts                -hosts                HOSTS                localhost,localhost.local    the ip or domains to bind to
Redis.Address        -redis-address        REDIS_ADDRESS        redis-master                 
Redis.Port           -redis-port           REDIS_PORT           6379                         
Redis.Password       -redis-password       REDIS_PASSWORD                                    
Redis.DB             -redis-db             REDIS_DB             0                            
Redis.Expire         -redis-expire         REDIS_EXPIRE         5s                           
Database.Address     -database-address     DATABASE_ADDRESS     localhost                    
Database.Port        -database-port        DATABASE_PORT        28015                        
Database.Database    -database-database    DATABASE_DATABASE    my-project                   

$ go run main.go 

{
 "Hosts": [
  "localhost",
  "localhost.local"
 ],
 "Redis": {
  "Address": "redis-master",
  "Port": "6379",
  "Password": "",
  "DB": 0,
  "Expire": 5000000000
 },
 "Database": {
  "Address": "localhost",
  "Port": "28015",
  "Database": "my-project"
 }
}

Custom names:

Sometimes you might want to use a different env var, or flag name for backwards compatibility or other reasons, you have two options.

  1. uconfig tag

You can change the name of a field as seen by unconfig. This option supports the usual nesting prefixing. See the port example below.

  1. Plugin specific tags

Most plugins support controlling the field name as seen by that specific plugin.

This option does not support nesting prefixes. See the Database field in the example below.

package database

// Config holds the database configurations.
type Database struct {
  Address  string `default:"localhost"`
  Port     string `default:"28015" uconfig:"Service.Port"`
  Database string `default:"my-project" env:"DB_NAME" flag:"main-db-name"`
}
package main

// Config is our application config.
type Config struct {

  // yes you can have slices.
  Hosts    []string `default:"localhost,localhost.local"`

  Redis    redis.Config
  Database database.Config
}

Which should give you the following settings:

Supported Fields:
FIELD                    FLAG                      ENV                      DEFAULT                      USAGE
-----                    -----                     -----                    -------                      -----
Hosts                    -hosts                    HOSTS                    localhost,localhost.local    the ip or domains to bind to
Redis.Port               -redis-port               REDIS_PORT               6379
Redis.Password           -redis-password           REDIS_PASSWORD
Redis.DB                 -redis-db                 REDIS_DB                 0
Redis.Expire             -redis-expire             REDIS_EXPIRE             5s
Database.Address         -database-address         DATABASE_ADDRESS         localhost
Database.Service.Port    -database-service-port    DATABASE_SERVICE_PORT    28015
Database.Database        -main-db-db               DB_NAME                  my-project
exit status 1

For file based plugins, you will need to use the appropriate tags as used by your encoder of choice. For example:

package users

// Config holds the database configurations.
type Config struct {
  Host string `json:"bind_addr"`
}

Secrets Plugin

GoDoc

The secret provider allows you to grab the value of a config from anywhere you want. You simply need to implement the func(name string) (value string) function and pass it to the secrets plugin.

Unlike most other plugins, secret requires explicit secret:"" tag, this is because only specific config values like passwords and api keys come from a secret provider, compared to the rest of the config which can be set in various ways.

import (

  "github.com/omeid/uconfig"
  "github.com/omeid/uconfig/plugins/secret"
)
// Creds is an example of a config struct that uses secret values.
type Creds struct {
  // by default, secret plugin will generate a name that is identical
  // to env plugin, SCREAM_SNAKE_CASE, so in this case it will be
  // APIKEY however, following the standard uConfig nesting rules
  // in Config struct below, it becomes CREDS_APIKEY.
  APIKey   string `secret:""`
  // or you can provide your own name, which will not be impacted
  // by nesting or the field name.
  APIToken string `secret:"API_TOKEN"`
}

type Config struct {
  Redis   Redis
  Creds   Creds
}


func main() {

  conf := &Config{}


  files := uconfig.Files{
    {"config.json", json.Unmarshal, false}
  }

   // secret.New accepts a function that maps a secret name to it's value.
   secretPlugin := secret.New(func(name string) (string, error) {
      // you're free to grab the secret based on the name from wherever
      // you please, aws secrets-manager, hashicorp vault, or wherever.
      value, ok := secretSource.Get(name)

      if !ok {
        return "", ErrSecretNotFound
      }

      return value, nil
  })

  // then you can use the secretPlugin with uConfig like any other plugin.
  // Lucky, uconfig.Classic allows passing more plugins, which means
  // you can simply do the following for flags, envs, files, and secrets!
  _, err := uconfig.Classic(&conf, files, secretPlugin)
  if err != nil {
    t.Fatal(err)
  }

}

Tests

For tests, you may consider the Must function to set the defaults, like so

package something

import (
  "testing"

  "github.com/omeid/uconfig"
  "github.com/omeid/uconfig/defaults"
)

func TestSomething(t *testing.T) error {

  conf := &YourConfigStruct{}

  // It will panic on error
  uconfig.Must(conf, defaults.New())

  // Use your conf as you please.
}

See the Classic source for how to compose plugins. For more details, see the godoc.

Extending uConfig:

uConfig provides a plugin mechanism for adding new sources of configuration. There are two kind of plugins, Walkers and Visitors.

To implement your own, see the examples.

Visitors

Visitors get a flat view of the configuration struct, which is a flat view of the structs regardless of nesting level, for more details see the flat package documentation.

Plugins that load the configurations from flat structures (e.g flags, environment variables, default tags) are good candidates for this type of plugin. See env plugin for an example.

Walkers

Walkers are used for configuration plugins that take the whole config struct and unmarshal the underlying content into the config struct. Plugins that load the configuration from files are good candidates for this.

See file plugin for an example.

uconfig's People

Contributors

akissa avatar andremissaglia avatar joncfoo avatar omeid avatar pauln avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

uconfig's Issues

Semver tags/releases (for Glide support)

I know this is quite a new project, but could you please add tags/releases (using semantic versioning) so that uconfig can be used via Glide without having to pin to specific commits?

go mod tidy seems to automatically get v1.2.0

Hello,

It seems that using uconfig causes go mod tidy to fetch v1.2.0 by default. At the very least, on go1.21.5 linux/amd64.

user@hostname > /tmp/foo > vim main.go # here just copy/paste the first full example from the README and remove redis/database references 
user@hostname > /tmp/foo > go mod init foo
go: creating new go.mod: module foo
go: to add module requirements and sums:
	go mod tidy
user@hostname > /tmp/foo > go mod tidy
go: finding module for package github.com/omeid/uconfig
go: found github.com/omeid/uconfig in github.com/omeid/uconfig v1.2.0
user@hostname > /tmp/foo > go version
go version go1.21.5 linux/amd64

This causes the version of uconfig to be somewhat outdated. I noticed this because the USAGE column was nowhere to be found upon calling .Usage() and decided to investigate a bit. Editing the go.mod to point towards 0.5 instead naturally works.

Can we not expose the FIELD column in the help document?

Can we not expose the FIELD column in the help document? Although the below looks good I don't think its a good idea to reveal how the conf is mapped to an internal data structure. I don't see any configuration library doing that however will leave it to the maintainer

Supported Fields:
FIELD                FLAG                  ENV                  DEFAULT                      USAGE
-----                -----                 -----                -------                      -----
Hosts                -hosts                HOSTS                localhost,localhost.local    the ip or domains to bind to
Redis.Address        -redis-address        REDIS_ADDRESS        redis-master                 
Redis.Port           -redis-port           REDIS_PORT           6379                         
Redis.Password       -redis-password       REDIS_PASSWORD                                    
Redis.DB             -redis-db             REDIS_DB             0                            
Redis.Expire         -redis-expire         REDIS_EXPIRE         5s                           
Database.Address     -database-address     DATABASE_ADDRESS     localhost                    
Database.Port        -database-port        DATABASE_PORT        28015                        
Database.Database    -database-database    DATABASE_DATABASE    my-project    

Improve Test Coverage

While there is some tests for uconfig, the overall test coverage could be improved.

unknown revision v1.2.0

My golang project fails to build as a result of an unknown revision of uconfig.

I have this in my go.mod require list: github.com/omeid/uconfig v1.2.0

Here are my terminal commands and output:
Screenshot 2023-10-04 at 10 19 30 AM

I'm using golang version 1.19.4 darwin/amd64

I see here https://pkg.go.dev/github.com/omeid/uconfig/plugins that you have the module registered under the version v1.2.0, but your tags in the repo do not match:
Screenshot 2023-10-04 at 11 08 46 AM

I think this is the source of the problem.

As a workaround, I've forked the repo and replaced the path with my forked repo like so: replace github.com/omeid/uconfig => github.com/Lumerin-protocol/uconfig v0.0.5

My team isn't happy with the workaround, so I'm just wondering if you could add a matching tag for v1.2.0. I think that would solve the problem. or register the package under v0.0.4, and please create a matching tag.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.