fixes, meaningful error messages and new features

This commit is contained in:
yusing
2024-03-27 06:30:47 +00:00
parent 539ef911de
commit 90f4aac946
50 changed files with 2079 additions and 885 deletions

View File

@@ -1,7 +1,6 @@
package main
import (
"fmt"
"os"
"sync"
@@ -14,48 +13,76 @@ type Config interface {
MustLoad()
GetAutoCertProvider() (AutoCertProvider, error)
// MustReload()
// Reload() error
Reload() error
StartProviders()
StopProviders()
WatchChanges()
StopWatching()
}
func NewConfig() Config {
cfg := &config{}
func NewConfig(path string) Config {
cfg := &config{reader: &FileReader{Path: path}}
cfg.watcher = NewFileWatcher(
configPath,
path,
cfg.MustReload, // OnChange
func() { os.Exit(1) }, // OnDelete
)
return cfg
}
func (cfg *config) Load() error {
func ValidateConfig(data []byte) error {
cfg := &config{reader: &ByteReader{data}}
return cfg.Load()
}
func (cfg *config) Load(reader ...Reader) error {
cfg.mutex.Lock()
defer cfg.mutex.Unlock()
// unload if any
cfg.StopProviders()
if cfg.reader == nil {
panic("config reader not set")
}
data, err := os.ReadFile(configPath)
data, err := cfg.reader.Read()
if err != nil {
return fmt.Errorf("unable to read config file: %v", err)
return NewNestedError("unable to read config file").With(err)
}
cfg.Providers = make(map[string]*Provider)
if err = yaml.Unmarshal(data, &cfg); err != nil {
return fmt.Errorf("unable to parse config file: %v", err)
model := &configModel{}
if err := yaml.Unmarshal(data, model); err != nil {
return NewNestedError("unable to parse config file").With(err)
}
for name, p := range cfg.Providers {
err := p.Init(name)
ne := NewNestedError("invalid config")
err = validateYaml(configSchema, data)
if err != nil {
ne.With(err)
}
pErrs := NewNestedError("errors in these providers")
for name, p := range model.Providers {
if p.Kind != ProviderKind_File {
continue
}
_, err := p.ValidateFile()
if err != nil {
cfgl.Errorf("failed to initialize provider %q %v", name, err)
cfg.Providers[name] = nil
pErrs.ExtraError(
NewNestedError("provider file validation error").
Subject(name).
With(err),
)
}
}
if pErrs.HasExtras() {
ne.With(pErrs)
}
if ne.HasInner() {
return ne
}
cfg.m = model
return nil
}
@@ -66,43 +93,92 @@ func (cfg *config) MustLoad() {
}
func (cfg *config) GetAutoCertProvider() (AutoCertProvider, error) {
return cfg.AutoCert.GetProvider()
return cfg.m.AutoCert.GetProvider()
}
func (cfg *config) Reload() error {
return cfg.Load()
cfg.StopProviders()
if err := cfg.Load(); err != nil {
return err
}
cfg.StartProviders()
return nil
}
func (cfg *config) MustReload() {
cfg.MustLoad()
if err := cfg.Reload(); err != nil {
cfgl.Fatal(err)
}
}
func (cfg *config) StartProviders() {
if cfg.Providers == nil {
cfgl.Fatal("providers not loaded")
if cfg.providerInitialized {
return
}
cfg.mutex.Lock()
defer cfg.mutex.Unlock()
if cfg.providerInitialized {
return
}
pErrs := NewNestedError("failed to start these providers")
ParallelForEachKeyValue(cfg.m.Providers, func(name string, p *Provider) {
err := p.Init(name)
if err != nil {
pErrs.ExtraError(NewNestedErrorFrom(err).Subjectf("%s providers %q", p.Kind, name))
delete(cfg.m.Providers, name)
}
p.StartAllRoutes()
})
cfg.providerInitialized = true
if pErrs.HasExtras() {
cfgl.Error(pErrs)
}
// Providers have their own mutex, no lock needed
ParallelForEachValue(cfg.Providers, (*Provider).StartAllRoutes)
}
func (cfg *config) StopProviders() {
if cfg.Providers != nil {
// Providers have their own mutex, no lock needed
ParallelForEachValue(cfg.Providers, (*Provider).StopAllRoutes)
if !cfg.providerInitialized {
return
}
cfg.mutex.Lock()
defer cfg.mutex.Unlock()
if !cfg.providerInitialized {
return
}
ParallelForEachValue(cfg.m.Providers, (*Provider).StopAllRoutes)
cfg.m.Providers = make(map[string]*Provider)
cfg.providerInitialized = false
}
func (cfg *config) WatchChanges() {
if cfg.watcher == nil {
return
}
cfg.watcher.Start()
}
func (cfg *config) StopWatching() {
if cfg.watcher == nil {
return
}
cfg.watcher.Stop()
}
type config struct {
Providers map[string]*Provider `yaml:",flow"`
AutoCert AutoCertConfig `yaml:",flow"`
watcher Watcher
mutex sync.Mutex
type configModel struct {
Providers map[string]*Provider `yaml:",flow" json:"providers"`
AutoCert AutoCertConfig `yaml:",flow" json:"autocert"`
}
type config struct {
m *configModel
reader Reader
watcher Watcher
mutex sync.Mutex
providerInitialized bool
}