package config import ( "fmt" "io" "io/fs" "os" "path/filepath" "github.com/google/uuid" yamlcodec "go.unistack.org/micro-codec-yaml/v3" 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 { ChecksFiles []string `json:"checks_files,omitempty" yaml:"checks_files,omitempty"` Checks []*CheckConfig `json:"checks,omitempty" yaml:"checks,omitempty"` MultiUser bool `json:"multi_user,omitempty" yaml:"multi_user,omitempty"` } type MeterConfig struct { Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"` } type CheckConfig struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` User string `json:"user,omitempty" yaml:"user,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"` Interval mtime.Duration `json:"interval,omitempty" yaml:"interval,omitempty"` Active bool `json:"active,omitempty" yaml:"active,omitempty"` } type HTTPConfig struct { Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"` Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` Data string `json:"data,omitempty" yaml:"data,omitempty"` Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` OpenAPI string `json:"openapi,omitempty" yaml:"openapi,omitempty"` Method string `json:"method,omitempty" yaml:"method,omitempty"` } type GRPCConfig struct { Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"` Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` Data string `json:"data,omitempty" yaml:"data,omitempty"` Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` Protoset string `json:"protoset,omitempty" yaml:"protoset,omitempty"` } type GraphQLConfig struct { Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"` Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` Data string `json:"data,omitempty" yaml:"data,omitempty"` Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` } type TaskConfig struct { HTTP *HTTPConfig `json:"http,omitempty" yaml:"http,omitempty"` GRPC *GRPCConfig `json:"grpc,omitempty" yaml:"grpc,omitempty"` GraphQL *GraphQLConfig `json:"graphql,omitempty" yaml:"graphql"` TLSVerify *bool `json:"tls_verify,omitempty" yaml:"tls_verify,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Timeout mtime.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` Active bool `json:"active,omitempty" yaml:"active,omitempty"` } func Load(fileSystem fs.FS, name string, cfg *Config) error { 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 { return err } 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 }