Add skeleton of a config

Took way longer than it should had because I was researching exisiting
options.

As it turns out, nothing nice exists that would handle:
 - Multiple overridiable config files
 - Defaults provided in a struct
 - Output in a struct

License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protonmail.ch>
This commit is contained in:
Jakub Sztandera 2019-07-03 18:59:49 +02:00
parent 46a5019c8d
commit 808a1e9deb
6 changed files with 148 additions and 0 deletions

View File

@ -18,6 +18,7 @@ linters:
issues:
exclude:
- "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this"
- "Potential file inclusion via variable"
exclude-use-default: false
exclude-rules:

5
go.mod
View File

@ -3,6 +3,9 @@ module github.com/filecoin-project/go-lotus
go 1.12
require (
github.com/BurntSushi/toml v0.3.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/ipfs/go-datastore v0.0.5
github.com/ipfs/go-ipfs-routing v0.1.0
github.com/ipfs/go-log v0.0.2-0.20190703113630-0c3cfb1eccc4
@ -25,9 +28,11 @@ require (
github.com/libp2p/go-maddr-filter v0.0.4
github.com/multiformats/go-multiaddr v0.0.4
github.com/multiformats/go-multihash v0.0.5
github.com/stretchr/testify v1.3.0
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
go.uber.org/dig v1.7.0 // indirect
go.uber.org/fx v1.9.0
go.uber.org/goleak v0.10.0 // indirect
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
gopkg.in/yaml.v2 v2.2.2 // indirect
)

7
go.sum
View File

@ -1,5 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
@ -22,6 +23,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0=
github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=
github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
@ -40,6 +43,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -364,4 +369,6 @@ gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 h1:Ggy3mWN4l3PUFPfSG0Y
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8/go.mod h1:cKXr3E0k4aosgycml1b5z33BVV6hai1Kh7uDgFOkbcs=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

38
node/config/def.go Normal file
View File

@ -0,0 +1,38 @@
package config
import "time"
// Root is starting point of the config
type Root struct {
API API
}
// API contains configs for API endpoint
type API struct {
ListenAddress string
Timeout Duration
}
// Default returns the default config
func Default() *Root {
def := Root{
API: API{
ListenAddress: "/ip6/::1/tcp/1234/http",
Timeout: Duration(30 * time.Second),
},
}
return &def
}
// Duration is a wrapper type for time.Duration for decoding it from TOML
type Duration time.Duration
// UnmarshalText implements interface for TOML decoding
func (dur *Duration) UnmarshalText(text []byte) error {
d, err := time.ParseDuration(string(text))
if err != nil {
return err
}
*dur = Duration(d)
return err
}

34
node/config/load.go Normal file
View File

@ -0,0 +1,34 @@
package config
import (
"io"
"os"
"github.com/BurntSushi/toml"
)
// FromFile loads config from a specified file overriding defaults specified in
// the source code. If file does not exist or is empty defaults are asummed.
func FromFile(path string) (*Root, error) {
file, err := os.Open(path)
switch {
case os.IsNotExist(err):
return Default(), nil
case err != nil:
return nil, err
}
defer file.Close() //nolint:errcheck // The file is RO
return FromReader(file)
}
// FromReader loads config from a reader instance.
func FromReader(reader io.Reader) (*Root, error) {
cfg := Default()
_, err := toml.DecodeReader(reader, cfg)
if err != nil {
return nil, err
}
return cfg, nil
}

63
node/config/load_test.go Normal file
View File

@ -0,0 +1,63 @@
package config
import (
"bytes"
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestDecodeNothing(t *testing.T) {
assert := assert.New(t)
{
cfg, err := FromFile(os.DevNull)
assert.Nil(err, "error should be nil")
assert.Equal(Default(), cfg,
"config from empty file should be the same as default")
}
{
cfg, err := FromFile("./does-not-exist.toml")
assert.Nil(err, "error should be nil")
assert.Equal(Default(), cfg,
"config from not exisiting file should be the same as default")
}
}
func TestParitalConfig(t *testing.T) {
assert := assert.New(t)
cfgString := `
[API]
Timeout = "10s"
`
expected := Default()
expected.API.Timeout = Duration(10 * time.Second)
{
cfg, err := FromReader(bytes.NewReader([]byte(cfgString)))
assert.NoError(err, "error should be nil")
assert.Equal(expected, cfg,
"config from reader should contain changes")
}
{
f, err := ioutil.TempFile("", "config-*.toml")
fname := f.Name()
assert.NoError(err, "tmp file shold not error")
_, err = f.WriteString(cfgString)
assert.NoError(err, "writing to tmp file should not error")
err = f.Close()
assert.NoError(err, "closing tmp file should not error")
defer os.Remove(fname) //nolint:errcheck
cfg, err := FromFile(fname)
assert.Nil(err, "error should be nil")
assert.Equal(expected, cfg,
"config from reader should contain changes")
}
}