allow to work with multiple configs

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2024-11-20 00:35:04 +03:00
parent 3398ee60f3
commit c1103c714a
4 changed files with 116 additions and 79 deletions

View File

@ -3,10 +3,8 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"io/fs"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"time" "time"
openapi_v3 "github.com/google/gnostic/openapiv3" openapi_v3 "github.com/google/gnostic/openapiv3"
@ -64,67 +62,14 @@ func main() {
l.Fatal(ctx, "failed to init meter", err) l.Fatal(ctx, "failed to init meter", err)
} }
meters["default"] = m meters[uuid.Nil.String()] = m
l.Info(ctx, "try to parse config.yaml")
f, err := os.Open("config.yaml")
if err != nil {
l.Fatal(ctx, "failed to open config", err)
}
cfg := &config.Config{} cfg := &config.Config{}
if err = config.Parse(f, cfg); err != nil { l.Info(ctx, "try to load config")
f.Close() if err := config.Load(config.Filesytem, "config.yaml", cfg); err != nil {
l.Fatal(ctx, "failed to open config", err) l.Fatal(ctx, "failed to open config", err)
} }
f.Close()
if cfg.App.ChecksDir != "" {
var configFiles []string
if !filepath.IsAbs(cfg.App.ChecksDir) {
dir, _ := os.Getwd()
cfg.App.ChecksDir = filepath.Clean(filepath.Join(dir, cfg.App.ChecksDir))
}
err = filepath.WalkDir(cfg.App.ChecksDir, func(path string, info fs.DirEntry, err error) error {
if err != nil {
return err
}
if info.IsDir() || !info.Type().IsRegular() {
return nil
}
if filepath.Ext(info.Name()) != ".yaml" {
return nil
}
configFiles = append(configFiles, path)
return nil
})
if err != nil {
l.Fatal(ctx, fmt.Sprintf("error loading config: %s", cfg.App.ChecksDir), err)
}
for _, configFile := range configFiles {
l.Info(ctx, "try to parse "+configFile)
f, err := os.Open(configFile)
if err != nil {
l.Fatal(ctx, "failed to open config", err)
}
checks := []*config.CheckConfig{}
if err = config.Parse(f, &checks); err != nil {
f.Close()
l.Fatal(ctx, "failed to open config", err)
}
f.Close()
cfg.App.Checks = append(cfg.App.Checks, checks...)
}
}
if !cfg.App.MultiUser { if !cfg.App.MultiUser {
m.Init( m.Init(
@ -214,7 +159,7 @@ func main() {
var mtr meter.Meter var mtr meter.Meter
if !cfg.App.MultiUser { if !cfg.App.MultiUser {
mtr = meters["default"] mtr = meters[uuid.Nil.String()]
} else { } else {
if v, ok := meters[check.User]; ok && v != nil { if v, ok := meters[check.User]; ok && v != nil {
mtr = v mtr = v

4
go.mod
View File

@ -12,6 +12,7 @@ require (
go.unistack.org/micro-codec-jsonpb/v3 v3.10.3 go.unistack.org/micro-codec-jsonpb/v3 v3.10.3
go.unistack.org/micro-codec-proto/v3 v3.10.2 go.unistack.org/micro-codec-proto/v3 v3.10.2
go.unistack.org/micro-codec-yaml/v3 v3.10.2 go.unistack.org/micro-codec-yaml/v3 v3.10.2
go.unistack.org/micro-config-file/v3 v3.8.10
go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9 go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9
go.unistack.org/micro-proto/v3 v3.4.1 go.unistack.org/micro-proto/v3 v3.4.1
go.unistack.org/micro-server-http/v3 v3.11.34 go.unistack.org/micro-server-http/v3 v3.11.34
@ -20,6 +21,7 @@ require (
) )
require ( require (
dario.cat/mergo v1.0.1 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
@ -29,7 +31,7 @@ require (
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/net v0.30.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.27.0 // indirect golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.20.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect
google.golang.org/grpc v1.68.0 // indirect google.golang.org/grpc v1.68.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

8
go.sum
View File

@ -593,6 +593,8 @@ cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vf
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
@ -908,6 +910,8 @@ go.unistack.org/micro-codec-proto/v3 v3.10.2 h1:9iUQjBjTsd/RgIqB5rAQMZE0CYWngoW9
go.unistack.org/micro-codec-proto/v3 v3.10.2/go.mod h1:54e1jb6aLL9obJUwJjtVupE5zY4PugTcMSqWDhz9aC4= go.unistack.org/micro-codec-proto/v3 v3.10.2/go.mod h1:54e1jb6aLL9obJUwJjtVupE5zY4PugTcMSqWDhz9aC4=
go.unistack.org/micro-codec-yaml/v3 v3.10.2 h1:02I9XzhaBHqZU8Vd5e2zhf8j4foJ4muPT/x4gdR6E4c= go.unistack.org/micro-codec-yaml/v3 v3.10.2 h1:02I9XzhaBHqZU8Vd5e2zhf8j4foJ4muPT/x4gdR6E4c=
go.unistack.org/micro-codec-yaml/v3 v3.10.2/go.mod h1:A/tYj7x9CRhuin7WxeIvnuo8bMDrZYcJkogVYN8X7rU= go.unistack.org/micro-codec-yaml/v3 v3.10.2/go.mod h1:A/tYj7x9CRhuin7WxeIvnuo8bMDrZYcJkogVYN8X7rU=
go.unistack.org/micro-config-file/v3 v3.8.10 h1:/IyD/i6I7Ic8jCNq7ZsTpWT8sToNG14gIFkSVPxbNpY=
go.unistack.org/micro-config-file/v3 v3.8.10/go.mod h1:w7uw5KxK3H2OrZwX4p0hQHbp9UzwDODYqJvdofySgxY=
go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9 h1:ZXCS0eFiSdvcFYxpxV2Q77gfwAjpIRydwAEI1QBrwuQ= go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9 h1:ZXCS0eFiSdvcFYxpxV2Q77gfwAjpIRydwAEI1QBrwuQ=
go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9/go.mod h1:xODJQ0Nu/F8k34D/z2ITL91OskI/C674XCkugAxmc3Q= go.unistack.org/micro-meter-victoriametrics/v3 v3.8.9/go.mod h1:xODJQ0Nu/F8k34D/z2ITL91OskI/C674XCkugAxmc3Q=
go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q= go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q=
@ -1202,8 +1206,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -1,14 +1,31 @@
package config package config
import ( import (
"fmt"
"io" "io"
"io/fs"
"os"
"path/filepath"
"github.com/google/uuid"
yamlcodec "go.unistack.org/micro-codec-yaml/v3" yamlcodec "go.unistack.org/micro-codec-yaml/v3"
mtime "go.unistack.org/micro/v3/util/time" mtime "go.unistack.org/micro/v3/util/time"
) )
var Filesytem fs.FS
func init() {
dir, _ := os.Getwd()
Filesytem = os.DirFS(dir)
}
type Config struct {
App *AppConfig `json:"app,omitempty" yaml:"app,omitempty"`
Meter *MeterConfig `json:"meter,omitempty" yaml:"meter,omitempty"`
}
type AppConfig struct { type AppConfig struct {
ChecksDir string `json:"checks_dir,omitempty" yaml:"checks_dir,omitempty"` ChecksFiles []string `json:"checks_files,omitempty" yaml:"checks_files,omitempty"`
Checks []*CheckConfig `json:"checks,omitempty" yaml:"checks,omitempty"` Checks []*CheckConfig `json:"checks,omitempty" yaml:"checks,omitempty"`
MultiUser bool `json:"multi_user,omitempty" yaml:"multi_user,omitempty"` MultiUser bool `json:"multi_user,omitempty" yaml:"multi_user,omitempty"`
} }
@ -18,18 +35,14 @@ type MeterConfig struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"`
} }
type Config struct {
App *AppConfig `json:"app,omitempty" yaml:"app,omitempty"`
Meter *MeterConfig `json:"meter,omitempty" yaml:"meter,omitempty"`
}
type CheckConfig struct { type CheckConfig struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"`
User string `json:"user,omitempty" yaml:"user,omitempty"`
Tasks []*TaskConfig `json:"tasks,omitempty" yaml:"tasks,omitempty"` Tasks []*TaskConfig `json:"tasks,omitempty" yaml:"tasks,omitempty"`
TasksFiles []string `json:"tasks_files,omitempty" yaml:"tasks_files,omitempty"`
Timeout mtime.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` Timeout mtime.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"`
Interval mtime.Duration `json:"interval,omitempty" yaml:"interval,omitempty"` Interval mtime.Duration `json:"interval,omitempty" yaml:"interval,omitempty"`
Active bool `json:"active,omitempty" yaml:"active,omitempty"` Active bool `json:"active,omitempty" yaml:"active,omitempty"`
User string `json:"user,omitempty" yaml:"user,omitempty"`
} }
type HTTPConfig struct { type HTTPConfig struct {
@ -66,11 +79,84 @@ type TaskConfig struct {
Active bool `json:"active,omitempty" yaml:"active,omitempty"` Active bool `json:"active,omitempty" yaml:"active,omitempty"`
} }
func Parse(r io.Reader, cfg interface{}) error { func Load(fileSystem fs.FS, name string, cfg *Config) error {
buf, err := io.ReadAll(r) if err := load(fileSystem, name, cfg); err != nil {
return err
}
for _, checksPatternFile := range cfg.App.ChecksFiles {
checkRoot := filepath.Dir(checksPatternFile)
checksFiles := fsWalkDir(fileSystem, checkRoot, checksPatternFile)
for _, checkFile := range checksFiles {
checks := []*CheckConfig{}
if err := load(fileSystem, checkFile, &checks); err != nil {
return err
}
for ckecksIdx := range checks {
for _, tasksPatternFile := range checks[ckecksIdx].TasksFiles {
taskRoot := filepath.Join(filepath.Dir(checksPatternFile), filepath.Dir(tasksPatternFile))
tasksFiles := fsWalkDir(fileSystem, taskRoot, filepath.Join(filepath.Dir(checksPatternFile), tasksPatternFile))
for tasksIdx := range tasksFiles {
tasks := []*TaskConfig{}
if err := load(fileSystem, tasksFiles[tasksIdx], &tasks); err != nil {
return err
}
checks[ckecksIdx].Tasks = append(checks[ckecksIdx].Tasks, tasks...)
}
}
cfg.App.Checks = append(cfg.App.Checks, checks[ckecksIdx])
}
}
}
if !cfg.App.MultiUser {
for _, check := range cfg.App.Checks {
check.User = uuid.Nil.String()
}
}
return nil
}
func fsWalkDir(fileSystem fs.FS, root string, pattern string) []string {
var files []string
fs.WalkDir(fileSystem, root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !d.Type().IsRegular() {
return nil
}
var ok bool
if ok, err = filepath.Match(pattern, path); err == nil && ok {
files = append(files, path)
} else {
return err
}
return nil
})
return files
}
func load(fileSystem fs.FS, name string, cfg interface{}) error {
f, err := fileSystem.Open(name)
if err != nil { if err != nil {
return err return err
} }
return yamlcodec.NewCodec().Unmarshal(buf, cfg) c := yamlcodec.NewCodec()
var buf []byte
if buf, err = io.ReadAll(f); err == nil {
if err = f.Close(); err == nil {
err = c.Unmarshal(buf, cfg)
}
}
if err != nil {
return fmt.Errorf("failed to load config %w", err)
}
return nil
} }