diff --git a/.drone.yml b/.drone.yml index 057d760..6182098 100644 --- a/.drone.yml +++ b/.drone.yml @@ -24,6 +24,6 @@ steps: - go test -parallel 10 -race ./... - name: golangci-lint - image: golangci/golangci-lint:v1.39 + image: golangci/golangci-lint:v1.53 commands: - golangci-lint run diff --git a/.golangci.yml b/.golangci.yml index 23e5557..29a6459 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -22,6 +22,8 @@ linters-settings: suggest-new: true misspell: locale: US + varnamelen: + min-name-length: 2 linters: enable-all: true @@ -30,6 +32,16 @@ linters: - maligned - interfacer - scopelint + - exhaustruct + - depguard + - nolintlint + #deprecated + - structcheck + - varcheck + - golint + - deadcode + - ifshort + - nosnakecase issues: # Excluding configuration per-path, per-linter, per-text and per-source @@ -39,8 +51,14 @@ issues: - gomnd - exhaustivestruct - wrapcheck + - exhaustruct + - varnamelen + - tenv + - funlen - path: test/* linters: - gomnd - exhaustivestruct - wrapcheck + - exhaustruct + - varnamelen diff --git a/client.go b/client.go index 434cfa2..508115e 100644 --- a/client.go +++ b/client.go @@ -49,17 +49,21 @@ type provider struct { factory func(ctx context.Context) (Provider, error) } -func (p *provider) init(ctx context.Context) (err error) { +func (p *provider) init(ctx context.Context) error { if atomic.LoadUint32(&p.done) == 0 { if !p.mu.TryLock() { return fmt.Errorf("%w", ErrInitFactory) } defer atomic.StoreUint32(&p.done, 1) defer p.mu.Unlock() - p.provider, err = p.factory(ctx) + + var err error + if p.provider, err = p.factory(ctx); err != nil { + return fmt.Errorf("init provider factory:%w", err) + } } - return err + return nil } func (p *provider) Watch(ctx context.Context, key Key, callback WatchCallback) error { @@ -67,8 +71,13 @@ func (p *provider) Watch(ctx context.Context, key Key, callback WatchCallback) e return fmt.Errorf("init read:%w", err) } - if watch, ok := p.provider.(WatchProvider); ok { - return watch.Watch(ctx, key, callback) + watch, ok := p.provider.(WatchProvider) + if !ok { + return nil + } + + if err := watch.Watch(ctx, key, callback); err != nil { + return fmt.Errorf("factory provider: %w", err) } return nil @@ -79,7 +88,12 @@ func (p *provider) Read(ctx context.Context, key Key) (Variable, error) { return Variable{}, fmt.Errorf("init read:%w", err) } - return p.provider.Read(ctx, key) + variable, err := p.provider.Read(ctx, key) + if err != nil { + return Variable{}, fmt.Errorf("factory provider: %w", err) + } + + return variable, nil } type Client struct { @@ -96,10 +110,12 @@ func (c *Client) key(name string) Key { } } +// Value get value by name. +// nolint: ireturn func (c *Client) Value(ctx context.Context, name string) (Value, error) { variable, err := c.Variable(ctx, name) if err != nil { - return nil, err + return nil, fmt.Errorf("variable:%w", err) } return variable.Value, nil diff --git a/client_example_test.go b/client_example_test.go index 33384f3..7550fb5 100644 --- a/client_example_test.go +++ b/client_example_test.go @@ -284,11 +284,11 @@ func ExampleClient_Value_factory() { fmt.Printf("listen from env: %d\n", port.Int()) fmt.Printf("title from json: %v\n", title.String()) - fmt.Printf("title from yaml: %v\n", yamlTitle.String()) + fmt.Printf("yaml title: %v\n", yamlTitle.String()) fmt.Printf("struct from json: %+v\n", cfg) // Output: // listen from env: 8080 // title from json: config title - // title from yaml: yaml title + // yaml title: yaml title // struct from json: {Duration:21m0s Enabled:true} } diff --git a/key/helpers.go b/key/helpers.go index 311fcfa..acd672f 100644 --- a/key/helpers.go +++ b/key/helpers.go @@ -9,14 +9,14 @@ import ( func LastIndex(sep string, factory config.KeyFactory) func(ctx context.Context, key config.Key) (string, string) { return func(ctx context.Context, key config.Key) (string, string) { - k := factory(ctx, key) + name := factory(ctx, key) - idx := strings.LastIndex(k, sep) + idx := strings.LastIndex(name, sep) if idx == -1 { - return k, "" + return name, "" } - return k[0:idx], k[idx+len(sep):] + return name[0:idx], name[idx+len(sep):] } } diff --git a/provider/arg/provider.go b/provider/arg/provider.go index 5164bd1..e106f05 100644 --- a/provider/arg/provider.go +++ b/provider/arg/provider.go @@ -21,15 +21,16 @@ func WithKeyFactory(factory config.KeyFactory) Option { } func New(opts ...Option) *Provider { - p := Provider{ - key: key.Name, + prov := Provider{ + key: key.Name, + args: make(map[string][]string, len(os.Args[1:])), } for _, opt := range opts { - opt(&p) + opt(&prov) } - return &p + return &prov } type Provider struct { @@ -38,9 +39,10 @@ type Provider struct { } // nolint: cyclop -func (p *Provider) parseOne(arg string) (name, val string, err error) { +// return name, value, error. +func (p *Provider) parseOne(arg string) (string, string, error) { if arg[0] != '-' { - return + return "", "", nil } numMinuses := 1 @@ -49,19 +51,21 @@ func (p *Provider) parseOne(arg string) (name, val string, err error) { numMinuses++ } - name = strings.TrimSpace(arg[numMinuses:]) + name := strings.TrimSpace(arg[numMinuses:]) if len(name) == 0 { - return + return name, "", nil } if name[0] == '-' || name[0] == '=' { return "", "", fmt.Errorf("%w: bad flag syntax: %s", config.ErrInvalidValue, arg) } - for i := 1; i < len(name); i++ { - if name[i] == '=' || name[i] == ' ' { - val = strings.TrimSpace(name[i+1:]) - name = name[0:i] + var val string + + for idx := 1; idx < len(name); idx++ { + if name[idx] == '=' || name[idx] == ' ' { + val = strings.TrimSpace(name[idx+1:]) + name = name[0:idx] break } @@ -79,8 +83,6 @@ func (p *Provider) parse() error { return nil } - p.args = make(map[string][]string, len(os.Args[1:])) - for _, arg := range os.Args[1:] { name, value, err := p.parseOne(arg) if err != nil { @@ -105,32 +107,36 @@ func (p *Provider) IsSupport(ctx context.Context, key config.Key) bool { func (p *Provider) Read(ctx context.Context, key config.Key) (config.Variable, error) { if err := p.parse(); err != nil { - return config.Variable{Provider: p.Name()}, err + return config.Variable{ + Name: "", + Value: nil, + Provider: p.Name(), + }, err } - k := p.key(ctx, key) - if val, ok := p.args[k]; ok { + name := p.key(ctx, key) + if val, ok := p.args[name]; ok { switch { case len(val) == 1: return config.Variable{ - Name: k, + Name: name, Provider: p.Name(), Value: value.JString(val[0]), }, nil default: - var n yaml.Node + var yNode yaml.Node - if err := yaml.Unmarshal([]byte("["+strings.Join(val, ",")+"]"), &n); err != nil { + if err := yaml.Unmarshal([]byte("["+strings.Join(val, ",")+"]"), &yNode); err != nil { return config.Variable{}, fmt.Errorf("arg: failed unmarshal yaml:%w", err) } return config.Variable{ - Name: k, + Name: name, Provider: p.Name(), - Value: value.Decode(n.Decode), + Value: value.Decode(yNode.Decode), }, nil } } - return config.Variable{Name: k, Provider: p.Name()}, config.ErrVariableNotFound + return config.Variable{}, fmt.Errorf("%w: %s", config.ErrVariableNotFound, name) } diff --git a/provider/env/provider.go b/provider/env/provider.go index 9102c16..e55520a 100644 --- a/provider/env/provider.go +++ b/provider/env/provider.go @@ -19,17 +19,17 @@ func WithKeyFactory(factory config.KeyFactory) Option { } func New(opts ...Option) *Provider { - p := Provider{ + provider := Provider{ key: func(ctx context.Context, k config.Key) string { return strings.ToUpper(key.NsAppName("_")(ctx, k)) }, } for _, opt := range opts { - opt(&p) + opt(&provider) } - return &p + return &provider } type Provider struct { @@ -45,17 +45,14 @@ func (p *Provider) IsSupport(ctx context.Context, key config.Key) bool { } func (p *Provider) Read(ctx context.Context, key config.Key) (config.Variable, error) { - k := p.key(ctx, key) - if val, ok := os.LookupEnv(k); ok { + name := p.key(ctx, key) + if val, ok := os.LookupEnv(name); ok { return config.Variable{ - Name: k, + Name: name, Provider: p.Name(), Value: value.JString(val), }, nil } - return config.Variable{ - Name: k, - Provider: p.Name(), - }, config.ErrVariableNotFound + return config.Variable{}, config.ErrVariableNotFound } diff --git a/provider/etcd/provider.go b/provider/etcd/provider.go index 0274356..91d187d 100644 --- a/provider/etcd/provider.go +++ b/provider/etcd/provider.go @@ -44,16 +44,16 @@ func (p *Provider) Name() string { } func (p *Provider) Read(ctx context.Context, key config.Key) (config.Variable, error) { - k := p.key(ctx, key) + name := p.key(ctx, key) - resp, err := p.client.Get(ctx, k, client.WithPrefix()) + resp, err := p.client.Get(ctx, name, client.WithPrefix()) if err != nil { - return config.Variable{}, fmt.Errorf("%w: key:%s, prov:%s", err, k, p.Name()) + return config.Variable{}, fmt.Errorf("%w: key:%s, prov:%s", err, name, p.Name()) } - val, err := p.resolve(k, resp.Kvs) + val, err := p.resolve(name, resp.Kvs) if err != nil { - return config.Variable{}, fmt.Errorf("%w: key:%s, prov:%s", err, k, p.Name()) + return config.Variable{}, fmt.Errorf("%w: key:%s, prov:%s", err, name, p.Name()) } return val, nil @@ -94,6 +94,7 @@ func (p *Provider) resolve(key string, kvs []*pb.KeyValue) (config.Variable, err return config.Variable{ Name: key, Provider: p.Name(), + Value: nil, }, nil case string(kv.Key) == key: return config.Variable{ @@ -104,8 +105,5 @@ func (p *Provider) resolve(key string, kvs []*pb.KeyValue) (config.Variable, err } } - return config.Variable{ - Name: key, - Provider: p.Name(), - }, config.ErrVariableNotFound + return config.Variable{}, fmt.Errorf("%w: name %s", config.ErrVariableNotFound, key) } diff --git a/provider/ini/provider.go b/provider/ini/provider.go index 01fed28..e06c15b 100644 --- a/provider/ini/provider.go +++ b/provider/ini/provider.go @@ -13,10 +13,12 @@ import ( var _ config.Provider = (*Provider)(nil) func New(data *ini.File) *Provider { + const nameParts = 2 + return &Provider{ data: data, resolve: func(ctx context.Context, key config.Key) (string, string) { - keys := strings.SplitN(key.Name, "/", 2) + keys := strings.SplitN(key.Name, "/", nameParts) if len(keys) == 1 { return "", keys[0] } @@ -46,12 +48,12 @@ func (p *Provider) Read(ctx context.Context, key config.Key) (config.Variable, e iniSection, err := p.data.GetSection(section) if err != nil { - return config.Variable{}, fmt.Errorf("%w: %s: %v", config.ErrVariableNotFound, p.Name(), err) + return config.Variable{}, fmt.Errorf("%w: %s: %w", config.ErrVariableNotFound, p.Name(), err) } iniKey, err := iniSection.GetKey(name) if err != nil { - return config.Variable{}, fmt.Errorf("%w: %s: %v", config.ErrVariableNotFound, p.Name(), err) + return config.Variable{}, fmt.Errorf("%w: %s: %w", config.ErrVariableNotFound, p.Name(), err) } return config.Variable{ diff --git a/provider/json/provider.go b/provider/json/provider.go index bacf6c2..13d1647 100644 --- a/provider/json/provider.go +++ b/provider/json/provider.go @@ -33,7 +33,7 @@ func NewFile(path string, opts ...Option) (*Provider, error) { return nil, fmt.Errorf("%w: unable to read config file %#q: file not found or unreadable", err, path) } - return New(file), nil + return New(file, opts...), nil } type Option func(*Provider) diff --git a/provider/toml/provider.go b/provider/toml/provider.go index 9a149a9..6129413 100644 --- a/provider/toml/provider.go +++ b/provider/toml/provider.go @@ -24,16 +24,16 @@ func NewFile(file string, opts ...Option) (*Provider, error) { type Option func(*Provider) func configure(tree *toml.Tree, opts ...Option) *Provider { - p := &Provider{ + prov := &Provider{ tree: tree, key: key.Name, } for _, opt := range opts { - opt(p) + opt(prov) } - return p + return prov } func New(data []byte, opts ...Option) (*Provider, error) { diff --git a/provider/toml/value.go b/provider/toml/value.go index cd310bd..7d2ab9d 100644 --- a/provider/toml/value.go +++ b/provider/toml/value.go @@ -30,11 +30,11 @@ func (s Value) ParseInt() (int, error) { func (s Value) Unmarshal(target interface{}) error { b, err := json.Marshal(s.Raw()) if err != nil { - return fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } if err := json.Unmarshal(b, target); err != nil { - return fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return nil diff --git a/provider/vault/secret.go b/provider/vault/secret.go index cfe09e8..0e56ce4 100644 --- a/provider/vault/secret.go +++ b/provider/vault/secret.go @@ -20,16 +20,16 @@ func WithSecretResolve(f func(context.Context, config.Key) (string, string)) Sec } func NewSecretKV2(client *api.Client, opts ...SecretOption) *SecretKV2 { - s := SecretKV2{ + prov := SecretKV2{ client: client, resolve: key.LastIndexField(":", "value", key.PrefixName("secret/data/", key.NsAppName("/"))), } for _, opt := range opts { - opt(&s) + opt(&prov) } - return &s + return &prov } type SecretKV2 struct { @@ -50,26 +50,26 @@ func (p *SecretKV2) Name() string { func (p *SecretKV2) Read(ctx context.Context, key config.Key) (config.Variable, error) { path, field := p.resolve(ctx, key) - s, err := p.client.Logical().Read(path) + secret, err := p.client.Logical().Read(path) if err != nil { return config.Variable{}, fmt.Errorf("%w: path:%s, field:%s, provider:%s", err, path, field, p.Name()) } - if s == nil || len(s.Data) == 0 { + if secret == nil || len(secret.Data) == 0 { return config.Variable{}, fmt.Errorf("%w: path:%s, field:%s, provider:%s", config.ErrVariableNotFound, path, field, p.Name()) } - if len(s.Warnings) > 0 { + if len(secret.Warnings) > 0 { return config.Variable{}, - fmt.Errorf("%w: warn: %s, path:%s, field:%s, provider:%s", config.ErrVariableNotFound, s.Warnings, path, field, p.Name()) + fmt.Errorf("%w: warn: %s, path:%s, field:%s, provider:%s", config.ErrVariableNotFound, secret.Warnings, path, field, p.Name()) } - d, ok := s.Data["data"].(map[string]interface{}) + data, ok := secret.Data["data"].(map[string]interface{}) if !ok { return config.Variable{}, fmt.Errorf("%w: path:%s, field:%s, provider:%s", config.ErrVariableNotFound, path, field, p.Name()) } - if val, ok := d[field]; ok { + if val, ok := data[field]; ok { return config.Variable{ Name: path + field, Provider: p.Name(), @@ -77,9 +77,9 @@ func (p *SecretKV2) Read(ctx context.Context, key config.Key) (config.Variable, }, nil } - md, err := json.Marshal(d) + md, err := json.Marshal(data) if err != nil { - return config.Variable{}, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return config.Variable{}, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return config.Variable{ diff --git a/provider/watcher/provider.go b/provider/watcher/provider.go index b97f923..fece95c 100644 --- a/provider/watcher/provider.go +++ b/provider/watcher/provider.go @@ -15,7 +15,7 @@ var ( ) func New(duration time.Duration, provider config.Provider, opts ...Option) *Provider { - p := &Provider{ + prov := &Provider{ Provider: provider, ticker: time.NewTicker(duration), logger: func(_ context.Context, msg string) { @@ -24,10 +24,10 @@ func New(duration time.Duration, provider config.Provider, opts ...Option) *Prov } for _, opt := range opts { - opt(p) + opt(prov) } - return p + return prov } func WithLogger(l func(context.Context, string)) Option { diff --git a/provider/watcher/provider_test.go b/provider/watcher/provider_test.go index 2cfaa6a..47611c9 100644 --- a/provider/watcher/provider_test.go +++ b/provider/watcher/provider_test.go @@ -22,12 +22,13 @@ func (p *provider) Name() string { return "test" } -func (p *provider) Read(ctx context.Context, k config.Key) (config.Variable, error) { +func (p *provider) Read(context.Context, config.Key) (config.Variable, error) { p.cnt++ return config.Variable{ - Name: "tmpname", - Value: value.JString(fmt.Sprint(p.cnt)), + Name: "tmpname", + Provider: p.Name(), + Value: value.JString(fmt.Sprint(p.cnt)), }, nil } diff --git a/provider/yaml/provider.go b/provider/yaml/provider.go index 4d70fcb..7e173a1 100644 --- a/provider/yaml/provider.go +++ b/provider/yaml/provider.go @@ -14,7 +14,7 @@ import ( var _ config.Provider = (*Provider)(nil) -func keyFactory(ctx context.Context, key config.Key) []string { +func keyFactory(_ context.Context, key config.Key) []string { return strings.Split(key.Name, "/") } @@ -37,15 +37,15 @@ func New(yml []byte, opts ...Option) (*Provider, error) { } func create(opts ...Option) *Provider { - p := Provider{ + prov := Provider{ key: keyFactory, } for _, opt := range opts { - opt(&p) + opt(&prov) } - return &p + return &prov } type Option func(*Provider) @@ -76,8 +76,8 @@ type node struct { *yaml.Node } -func (n *node) read(name string, k []string) (config.Variable, error) { - val, err := getData(n.Node.Content[0].Content, k) +func (n *node) read(name string, keys []string) (config.Variable, error) { + val, err := getData(n.Node.Content[0].Content, keys) if err != nil { if errors.Is(err, config.ErrVariableNotFound) { return config.Variable{}, fmt.Errorf("%w: %s", config.ErrVariableNotFound, name) @@ -87,20 +87,20 @@ func (n *node) read(name string, k []string) (config.Variable, error) { } return config.Variable{ - Name: strings.Join(k, "."), + Name: strings.Join(keys, "."), Provider: name, Value: value.Decode(val), }, nil } func getData(node []*yaml.Node, keys []string) (func(interface{}) error, error) { - for i := len(node) - 1; i > 0; i -= 2 { - if node[i-1].Value == keys[0] { + for idx := len(node) - 1; idx > 0; idx -= 2 { + if node[idx-1].Value == keys[0] { if len(keys) > 1 { - return getData(node[i].Content, keys[1:]) + return getData(node[idx].Content, keys[1:]) } - return node[i].Decode, nil + return node[idx].Decode, nil } } diff --git a/provider/yaml/watch.go b/provider/yaml/watch.go index 624a094..2da862a 100644 --- a/provider/yaml/watch.go +++ b/provider/yaml/watch.go @@ -33,10 +33,10 @@ func (p *Watch) Read(ctx context.Context, key config.Key) (config.Variable, erro return config.Variable{}, fmt.Errorf("yaml_file: read error: %w", err) } - var n yaml.Node - if err = yaml.Unmarshal(in, &n); err != nil { + var yNode yaml.Node + if err = yaml.Unmarshal(in, &yNode); err != nil { return config.Variable{}, fmt.Errorf("yaml_file: unmarshal error: %w", err) } - return p.prov.With(&n).Read(ctx, key) + return p.prov.With(&yNode).Read(ctx, key) } diff --git a/value/decode.go b/value/decode.go index 6c30114..8258fda 100644 --- a/value/decode.go +++ b/value/decode.go @@ -1,3 +1,4 @@ +// nolint: nonamedreturns package value import ( diff --git a/value/empty.go b/value/empty.go index dac3e27..6b889ad 100644 --- a/value/empty.go +++ b/value/empty.go @@ -8,7 +8,7 @@ type Empty struct { Err error } -func (e Empty) Unmarshal(val interface{}) error { +func (e Empty) Unmarshal(_ interface{}) error { return e.Err } diff --git a/value/helpers.go b/value/helpers.go index e03bed7..5e93d09 100644 --- a/value/helpers.go +++ b/value/helpers.go @@ -12,7 +12,7 @@ import ( func ParseDuration(raw string) (time.Duration, error) { d, err := time.ParseDuration(raw) if err != nil { - return 0, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return 0, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return d, nil @@ -21,7 +21,7 @@ func ParseDuration(raw string) (time.Duration, error) { func ParseInt(s string) (int64, error) { i, err := strconv.ParseInt(s, 10, 64) if err != nil { - return 0, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return 0, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return i, nil @@ -30,7 +30,7 @@ func ParseInt(s string) (int64, error) { func ParseUint(s string) (uint64, error) { i, err := strconv.ParseUint(s, 10, 64) if err != nil { - return 0, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return 0, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return i, nil @@ -39,7 +39,7 @@ func ParseUint(s string) (uint64, error) { func Atoi(s string) (int, error) { i, err := strconv.Atoi(s) if err != nil { - return 0, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return 0, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return i, nil @@ -48,7 +48,7 @@ func Atoi(s string) (int, error) { func ParseTime(s string) (time.Time, error) { i, err := time.Parse(time.RFC3339, s) if err != nil { - return time.Time{}, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return time.Time{}, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return i, nil @@ -57,7 +57,7 @@ func ParseTime(s string) (time.Time, error) { func ParseFloat(s string) (float64, error) { f, err := strconv.ParseFloat(s, 64) if err != nil { - return 0, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return 0, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return f, nil @@ -66,7 +66,7 @@ func ParseFloat(s string) (float64, error) { func ParseBool(s string) (bool, error) { b, err := strconv.ParseBool(s) if err != nil { - return false, fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return false, fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return b, nil @@ -74,7 +74,7 @@ func ParseBool(s string) (bool, error) { func JUnmarshal(b []byte, v interface{}) error { if err := json.Unmarshal(b, v); err != nil { - return fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return nil diff --git a/value/value.go b/value/value.go index ac0f24b..727fd86 100644 --- a/value/value.go +++ b/value/value.go @@ -76,7 +76,7 @@ func (s Value) Unmarshal(target interface{}) error { if v, ok := s.Raw().([]byte); ok { err := json.Unmarshal(v, target) if err != nil { - return fmt.Errorf("%w: %s", config.ErrInvalidValue, err) + return fmt.Errorf("%w: %w", config.ErrInvalidValue, err) } return nil