add config
This commit is contained in:
47
config/source/flag/README.md
Normal file
47
config/source/flag/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Flag Source
|
||||
|
||||
The flag source reads config from flags
|
||||
|
||||
## Format
|
||||
|
||||
We expect the use of the `flag` package. Upper case flags will be lower cased. Dashes will be used as delimiters.
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
dbAddress := flag.String("database_address", "127.0.0.1", "the db address")
|
||||
dbPort := flag.Int("database_port", 3306, "the db port)
|
||||
```
|
||||
|
||||
Becomes
|
||||
|
||||
```json
|
||||
{
|
||||
"database": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 3306
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## New Source
|
||||
|
||||
```go
|
||||
flagSource := flag.NewSource(
|
||||
// optionally enable reading of unset flags and their default
|
||||
// values into config, defaults to false
|
||||
IncludeUnset(true)
|
||||
)
|
||||
```
|
||||
|
||||
## Load Source
|
||||
|
||||
Load the source into config
|
||||
|
||||
```go
|
||||
// Create new config
|
||||
conf := config.NewConfig()
|
||||
|
||||
// Load file source
|
||||
conf.Load(flagSource)
|
||||
```
|
97
config/source/flag/flag.go
Normal file
97
config/source/flag/flag.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type flagsrc struct {
|
||||
opts source.Options
|
||||
}
|
||||
|
||||
func (fs *flagsrc) Read() (*source.ChangeSet, error) {
|
||||
if !flag.Parsed() {
|
||||
return nil, errors.New("flags not parsed")
|
||||
}
|
||||
|
||||
var changes map[string]interface{}
|
||||
|
||||
visitFn := func(f *flag.Flag) {
|
||||
n := strings.ToLower(f.Name)
|
||||
keys := strings.FieldsFunc(n, split)
|
||||
reverse(keys)
|
||||
|
||||
tmp := make(map[string]interface{})
|
||||
for i, k := range keys {
|
||||
if i == 0 {
|
||||
tmp[k] = f.Value
|
||||
continue
|
||||
}
|
||||
|
||||
tmp = map[string]interface{}{k: tmp}
|
||||
}
|
||||
|
||||
mergo.Map(&changes, tmp) // need to sort error handling
|
||||
return
|
||||
}
|
||||
|
||||
unset, ok := fs.opts.Context.Value(includeUnsetKey{}).(bool)
|
||||
if ok && unset {
|
||||
flag.VisitAll(visitFn)
|
||||
} else {
|
||||
flag.Visit(visitFn)
|
||||
}
|
||||
|
||||
b, err := fs.opts.Encoder.Encode(changes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Format: fs.opts.Encoder.String(),
|
||||
Data: b,
|
||||
Timestamp: time.Now(),
|
||||
Source: fs.String(),
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
func split(r rune) bool {
|
||||
return r == '-' || r == '_'
|
||||
}
|
||||
|
||||
func reverse(ss []string) {
|
||||
for i := len(ss)/2 - 1; i >= 0; i-- {
|
||||
opp := len(ss) - 1 - i
|
||||
ss[i], ss[opp] = ss[opp], ss[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *flagsrc) Watch() (source.Watcher, error) {
|
||||
return source.NewNoopWatcher()
|
||||
}
|
||||
|
||||
func (fs *flagsrc) String() string {
|
||||
return "flag"
|
||||
}
|
||||
|
||||
// NewSource returns a config source for integrating parsed flags.
|
||||
// Hyphens are delimiters for nesting, and all keys are lowercased.
|
||||
//
|
||||
// Example:
|
||||
// dbhost := flag.String("database-host", "localhost", "the db host name")
|
||||
//
|
||||
// {
|
||||
// "database": {
|
||||
// "host": "localhost"
|
||||
// }
|
||||
// }
|
||||
func NewSource(opts ...source.Option) source.Source {
|
||||
return &flagsrc{opts: source.NewOptions(opts...)}
|
||||
}
|
66
config/source/flag/flag_test.go
Normal file
66
config/source/flag/flag_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
dbuser = flag.String("database-user", "default", "db user")
|
||||
dbhost = flag.String("database-host", "", "db host")
|
||||
dbpw = flag.String("database-password", "", "db pw")
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Set("database-host", "localhost")
|
||||
flag.Set("database-password", "some-password")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func TestFlagsrc_Read(t *testing.T) {
|
||||
source := NewSource()
|
||||
c, err := source.Read()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var actual map[string]interface{}
|
||||
if err := json.Unmarshal(c.Data, &actual); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
actualDB := actual["database"].(map[string]interface{})
|
||||
if actualDB["host"] != *dbhost {
|
||||
t.Errorf("expected %v got %v", *dbhost, actualDB["host"])
|
||||
}
|
||||
|
||||
if actualDB["password"] != *dbpw {
|
||||
t.Errorf("expected %v got %v", *dbpw, actualDB["password"])
|
||||
}
|
||||
|
||||
// unset flags should not be loaded
|
||||
if actualDB["user"] != nil {
|
||||
t.Errorf("expected %v got %v", nil, actualDB["user"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagsrc_ReadAll(t *testing.T) {
|
||||
source := NewSource(IncludeUnset(true))
|
||||
c, err := source.Read()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var actual map[string]interface{}
|
||||
if err := json.Unmarshal(c.Data, &actual); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
actualDB := actual["database"].(map[string]interface{})
|
||||
|
||||
// unset flag defaults should be loaded
|
||||
if actualDB["user"] != *dbuser {
|
||||
t.Errorf("expected %v got %v", *dbuser, actualDB["user"])
|
||||
}
|
||||
}
|
20
config/source/flag/options.go
Normal file
20
config/source/flag/options.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
type includeUnsetKey struct{}
|
||||
|
||||
// IncludeUnset toggles the loading of unset flags and their respective default values.
|
||||
// Default behavior is to ignore any unset flags.
|
||||
func IncludeUnset(b bool) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, includeUnsetKey{}, true)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user