Skip to content

Commit

Permalink
Merge pull request #84 from osspkg/add-config-resolvers
Browse files Browse the repository at this point in the history
Add config resolvers
  • Loading branch information
markus621 committed Mar 2, 2024
2 parents f0fcfe2 + 026b699 commit 7b543d8
Show file tree
Hide file tree
Showing 30 changed files with 347 additions and 85 deletions.
59 changes: 40 additions & 19 deletions app/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
package app

import (
"go.osspkg.com/goppy/config"
"go.osspkg.com/goppy/console"
"go.osspkg.com/goppy/env"
"go.osspkg.com/goppy/iofile"
"go.osspkg.com/goppy/syscall"
"go.osspkg.com/goppy/xc"
"go.osspkg.com/goppy/xlog"
Expand All @@ -18,7 +18,9 @@ type (
App interface {
Logger(log xlog.Logger) App
Modules(modules ...interface{}) App
ConfigFile(filename string, configs ...interface{}) App
ConfigResolvers(res ...config.Resolver) App
ConfigFile(filename string) App
ConfigModels(configs ...interface{}) App
PidFile(filename string) App
Run()
Invoke(call interface{})
Expand All @@ -29,9 +31,9 @@ type (
_app struct {
configFilePath string
pidFilePath string
resolvers []config.Resolver
configs Modules
modules Modules
sources iofile.FileCodec
packages Container
logHandler *_log
log xlog.Logger
Expand All @@ -44,6 +46,7 @@ type (
func New() App {
ctx := xc.New()
return &_app{
resolvers: make([]config.Resolver, 0, 2),
modules: Modules{},
configs: Modules{},
packages: NewContainer(ctx),
Expand All @@ -68,17 +71,28 @@ func (a *_app) Modules(modules ...interface{}) App {
a.modules = a.modules.Add(v)
}
}

return a
}

// ConfigFile set config file path and configs models
func (a *_app) ConfigFile(filename string, configs ...interface{}) App {
// ConfigFile set config file path
func (a *_app) ConfigFile(filename string) App {
a.configFilePath = filename
for _, config := range configs {
a.configs = a.configs.Add(config)
return a
}

// ConfigModels set configs models
func (a *_app) ConfigModels(configs ...interface{}) App {
for _, c := range configs {
a.configs = a.configs.Add(c)
}
return a
}

// ConfigResolvers set configs resolvers
func (a *_app) ConfigResolvers(crs ...config.Resolver) App {
for _, r := range crs {
a.resolvers = append(a.resolvers, r)
}
return a
}

Expand Down Expand Up @@ -207,29 +221,36 @@ func (a *_app) prepareConfig(interactive bool) {
}
if len(a.configFilePath) > 0 {
// read config file
a.sources = iofile.FileCodec(a.configFilePath)

// init logger
config := &Config{}
if err = a.sources.Decode(config); err != nil {
resolver := config.NewConfigResolve(a.resolvers...)
if err = resolver.OpenFile(a.configFilePath); err != nil {
console.FatalIfErr(err, "open config file: %s", a.configFilePath)
}
if err = resolver.Build(); err != nil {
console.FatalIfErr(err, "prepare config file: %s", a.configFilePath)
}
appConfig := &Config{}
if err = resolver.Decode(appConfig); err != nil {
console.FatalIfErr(err, "decode config file: %s", a.configFilePath)
}
if interactive {
config.Log.Level = 4
config.Log.FilePath = "/dev/stdout"
appConfig.Log.Level = 4
appConfig.Log.FilePath = "/dev/stdout"
}
a.logHandler = newLog(config.Log)

// init logger
a.logHandler = newLog(appConfig.Log)
if a.log == nil {
a.log = xlog.Default()
}
a.logHandler.Handler(a.log)
a.modules = a.modules.Add(
env.ENV(config.Env),
env.ENV(appConfig.Env),
)

// decode all configs
var configs []interface{}
configs, err = typingReflectPtr(a.configs, func(i interface{}) error {
return a.sources.Decode(i)
configs, err = typingReflectPtr(a.configs, func(c interface{}) error {
return resolver.Decode(c)
})
if err != nil {
a.log.WithFields(xlog.Fields{
Expand Down
5 changes: 3 additions & 2 deletions app/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module go.osspkg.com/goppy/app
go 1.20

replace (
go.osspkg.com/goppy/config => ../config
go.osspkg.com/goppy/console => ../console
go.osspkg.com/goppy/env => ../env
go.osspkg.com/goppy/errors => ../errors
Expand All @@ -16,14 +17,14 @@ replace (

require (
go.osspkg.com/algorithms v1.3.1
go.osspkg.com/goppy/config v0.0.1
go.osspkg.com/goppy/console v0.3.1
go.osspkg.com/goppy/env v0.3.0
go.osspkg.com/goppy/errors v0.3.0
go.osspkg.com/goppy/iofile v0.3.1
go.osspkg.com/goppy/iosync v0.3.0
go.osspkg.com/goppy/syscall v0.3.0
go.osspkg.com/goppy/xc v0.3.0
go.osspkg.com/goppy/xlog v0.3.1
go.osspkg.com/goppy/xlog v0.3.2
go.osspkg.com/goppy/xtest v0.3.0
)

Expand Down
4 changes: 2 additions & 2 deletions auth/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
go.osspkg.com/goppy/ioutil v0.3.0
go.osspkg.com/goppy/plugins v0.3.1
go.osspkg.com/goppy/random v0.3.0
go.osspkg.com/goppy/web v0.3.2
go.osspkg.com/goppy/web v0.3.3
go.osspkg.com/goppy/xtest v0.3.0
golang.org/x/oauth2 v0.16.0
)
Expand All @@ -36,7 +36,7 @@ require (
go.osspkg.com/goppy/iosync v0.3.0 // indirect
go.osspkg.com/goppy/syscall v0.3.0 // indirect
go.osspkg.com/goppy/xc v0.3.0 // indirect
go.osspkg.com/goppy/xlog v0.3.1 // indirect
go.osspkg.com/goppy/xlog v0.3.2 // indirect
go.osspkg.com/goppy/xnet v0.3.0 // indirect
go.osspkg.com/static v1.4.0 // indirect
golang.org/x/net v0.20.0 // indirect
Expand Down
48 changes: 48 additions & 0 deletions config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Config Resolver

Updating the config through resolver variables.

## Config example

update config via ENV

```text
@env(key#defaut value)
```

```yaml
envs:
home: "@env(HOME#/tmp/home)"
path: "@env(PATH#/usr/local/bin)"
```

```go
import (
"go.osspkg.com/goppy/config"
)

type (
ConfigItem struct {
Home string `yaml:"home"`
Path string `yaml:"path"`
}
Config struct {
Envs testConfigItem `yaml:"envs"`
}
)

func main() {
conf := Config{}

res := config.NewConfigResolve(
config.EnvResolver(), // env resolver
)
res.OpenFile("./config.yaml") // open config file
res.Build() // prepare config with resolvers
res.Decode(&conf) // decoding config

fmt.Println(conf.Envs.Home)
fmt.Println(conf.Envs.Path)
}

```
76 changes: 76 additions & 0 deletions config/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2022-2024 Mikhail Knyazhev <markus621@yandex.com>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package config

import (
"bytes"
"fmt"
"os"
"regexp"

"gopkg.in/yaml.v3"
)

type (
Resolver interface {
Name() string
Value(name string) (string, bool)
}

Config struct {
blob []byte
resolvers []Resolver
}
)

func NewConfigResolve(rs ...Resolver) *Config {
return &Config{
blob: nil,
resolvers: rs,
}
}

func (v *Config) OpenFile(filename string) error {
b, err := os.ReadFile(filename)
if err != nil {
return err
}
v.blob = b
return nil
}

func (v *Config) Decode(cs ...interface{}) error {
for _, c := range cs {
if err := yaml.Unmarshal(v.blob, c); err != nil {
return err
}
}
return nil
}

var rexName = regexp.MustCompile(`(?m)^[a-z][a-z0-9]+$`)

func (v *Config) Build() error {
for _, resolver := range v.resolvers {
if !rexName.MatchString(resolver.Name()) {
return fmt.Errorf("resolver '%s' has invalid name, must like regexp [a-z][a-z0-9]+", resolver.Name())
}
rex := regexp.MustCompile(fmt.Sprintf(`(?mUsi)@%s\((.+)#(.*)\)`, resolver.Name()))
submatchs := rex.FindAllSubmatch(v.blob, -1)

for _, submatch := range submatchs {
pattern, key, defval := submatch[0], submatch[1], submatch[2]

if val, ok := resolver.Value(string(key)); ok && len(val) > 0 {
defval = []byte(val)
}

v.blob = bytes.ReplaceAll(v.blob, pattern, defval)
}
}

return nil
}
49 changes: 49 additions & 0 deletions config/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022-2024 Mikhail Knyazhev <markus621@yandex.com>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package config_test

import (
"os"
"testing"

"go.osspkg.com/goppy/config"
"go.osspkg.com/goppy/xtest"
)

type (
testConfigItem struct {
Home string `yaml:"home"`
Path string `yaml:"path"`
}
testConfig struct {
Envs testConfigItem `yaml:"envs"`
}
)

func TestUnit_ConfigResolve(t *testing.T) {
filename := "/tmp/TestUnit_ConfigResolve.yaml"
data := `
envs:
home: "@env(HOME#fail)"
path: "@env(PATH#fail)"
`
err := os.WriteFile(filename, []byte(data), 0755)
xtest.NoError(t, err)

res := config.NewConfigResolve(config.EnvResolver())

err = res.OpenFile(filename)
xtest.NoError(t, err)
err = res.Build()
xtest.NoError(t, err)

tc := &testConfig{}

err = res.Decode(tc)
xtest.NoError(t, err)
xtest.NotEqual(t, "fail", tc.Envs.Home)
xtest.NotEqual(t, "fail", tc.Envs.Path)
}
16 changes: 16 additions & 0 deletions config/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module go.osspkg.com/goppy/config

go 1.20

replace go.osspkg.com/goppy/xtest => ./../xtest

require (
go.osspkg.com/goppy/xtest v0.3.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/kr/pretty v0.3.1 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
17 changes: 17 additions & 0 deletions config/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 7b543d8

Please sign in to comment.