mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-25 18:41:10 +01:00
v0.5.0-rc5: check release
This commit is contained in:
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/yusing/go-proxy/autocert"
|
||||
@@ -17,32 +18,26 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
value *M.Config
|
||||
|
||||
l logrus.FieldLogger
|
||||
reader U.Reader
|
||||
proxyProviders *F.Map[string, *PR.Provider]
|
||||
value *M.Config
|
||||
proxyProviders F.Map[string, *PR.Provider]
|
||||
autocertProvider *autocert.Provider
|
||||
|
||||
l logrus.FieldLogger
|
||||
|
||||
watcher W.Watcher
|
||||
watcherCtx context.Context
|
||||
watcherCancel context.CancelFunc
|
||||
reloadReq chan struct{}
|
||||
}
|
||||
|
||||
func New() (*Config, E.NestedError) {
|
||||
func Load() (*Config, E.NestedError) {
|
||||
cfg := &Config{
|
||||
l: logrus.WithField("module", "config"),
|
||||
reader: U.NewFileReader(common.ConfigPath),
|
||||
watcher: W.NewFileWatcher(common.ConfigFileName),
|
||||
reloadReq: make(chan struct{}, 1),
|
||||
proxyProviders: F.NewMapOf[string, *PR.Provider](),
|
||||
l: logrus.WithField("module", "config"),
|
||||
watcher: W.NewFileWatcher(common.ConfigFileName),
|
||||
reloadReq: make(chan struct{}, 1),
|
||||
}
|
||||
if err := cfg.load(); err.HasError() {
|
||||
return nil, err
|
||||
}
|
||||
cfg.startProviders()
|
||||
cfg.watchChanges()
|
||||
return cfg, E.Nil()
|
||||
return cfg, cfg.load()
|
||||
}
|
||||
|
||||
func Validate(data []byte) E.NestedError {
|
||||
@@ -57,11 +52,17 @@ func (cfg *Config) GetAutoCertProvider() *autocert.Provider {
|
||||
return cfg.autocertProvider
|
||||
}
|
||||
|
||||
func (cfg *Config) StartProxyProviders() {
|
||||
cfg.startProviders()
|
||||
cfg.watchChanges()
|
||||
}
|
||||
|
||||
func (cfg *Config) Dispose() {
|
||||
cfg.watcherCancel()
|
||||
cfg.l.Debug("stopped watcher")
|
||||
if cfg.watcherCancel != nil {
|
||||
cfg.watcherCancel()
|
||||
cfg.l.Debug("stopped watcher")
|
||||
}
|
||||
cfg.stopProviders()
|
||||
cfg.l.Debug("stopped providers")
|
||||
}
|
||||
|
||||
func (cfg *Config) Reload() E.NestedError {
|
||||
@@ -70,46 +71,31 @@ func (cfg *Config) Reload() E.NestedError {
|
||||
return err
|
||||
}
|
||||
cfg.startProviders()
|
||||
return E.Nil()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) FindRoute(alias string) R.Route {
|
||||
r := cfg.proxyProviders.Find(
|
||||
func(p *PR.Provider) (any, bool) {
|
||||
rs := p.GetCurrentRoutes()
|
||||
if rs.Contains(alias) {
|
||||
return rs.Get(alias), true
|
||||
return F.MapFind(cfg.proxyProviders,
|
||||
func(p *PR.Provider) (R.Route, bool) {
|
||||
if route, ok := p.GetRoute(alias); ok {
|
||||
return route, true
|
||||
}
|
||||
return nil, false
|
||||
},
|
||||
)
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return r.(R.Route)
|
||||
}
|
||||
|
||||
func (cfg *Config) RoutesByAlias() map[string]U.SerializedObject {
|
||||
routes := make(map[string]U.SerializedObject)
|
||||
cfg.proxyProviders.Each(func(p *PR.Provider) {
|
||||
prName := p.GetName()
|
||||
p.GetCurrentRoutes().EachKV(func(a string, r R.Route) {
|
||||
obj, err := U.Serialize(r)
|
||||
if err.HasError() {
|
||||
cfg.l.Error(err)
|
||||
return
|
||||
}
|
||||
obj["provider"] = prName
|
||||
switch r.(type) {
|
||||
case *R.StreamRoute:
|
||||
obj["type"] = "stream"
|
||||
case *R.HTTPRoute:
|
||||
obj["type"] = "reverse_proxy"
|
||||
default:
|
||||
panic("bug: should not reach here")
|
||||
}
|
||||
routes[a] = obj
|
||||
})
|
||||
cfg.forEachRoute(func(alias string, r R.Route, p *PR.Provider) {
|
||||
obj, err := U.Serialize(r)
|
||||
if err.HasError() {
|
||||
cfg.l.Error(err)
|
||||
return
|
||||
}
|
||||
obj["provider"] = p.GetName()
|
||||
obj["type"] = string(r.Type())
|
||||
routes[alias] = obj
|
||||
})
|
||||
return routes
|
||||
}
|
||||
@@ -119,26 +105,23 @@ func (cfg *Config) Statistics() map[string]any {
|
||||
nTotalRPs := 0
|
||||
providerStats := make(map[string]any)
|
||||
|
||||
cfg.proxyProviders.Each(func(p *PR.Provider) {
|
||||
stats := make(map[string]any)
|
||||
nStreams := 0
|
||||
nRPs := 0
|
||||
p.GetCurrentRoutes().EachKV(func(a string, r R.Route) {
|
||||
switch r.(type) {
|
||||
case *R.StreamRoute:
|
||||
nStreams++
|
||||
nTotalStreams++
|
||||
case *R.HTTPRoute:
|
||||
nRPs++
|
||||
nTotalRPs++
|
||||
default:
|
||||
panic("bug: should not reach here")
|
||||
}
|
||||
})
|
||||
stats["type"] = p.GetType()
|
||||
stats["num_streams"] = nStreams
|
||||
stats["num_reverse_proxies"] = nRPs
|
||||
providerStats[p.GetName()] = stats
|
||||
cfg.forEachRoute(func(alias string, r R.Route, p *PR.Provider) {
|
||||
s, ok := providerStats[p.GetName()]
|
||||
if !ok {
|
||||
s = make(map[string]int)
|
||||
}
|
||||
|
||||
stats := s.(map[string]int)
|
||||
switch r.Type() {
|
||||
case R.RouteTypeStream:
|
||||
stats["num_streams"]++
|
||||
nTotalStreams++
|
||||
case R.RouteTypeReverseProxy:
|
||||
stats["num_reverse_proxies"]++
|
||||
nTotalRPs++
|
||||
default:
|
||||
panic("bug: should not reach here")
|
||||
}
|
||||
})
|
||||
|
||||
return map[string]any{
|
||||
@@ -148,6 +131,14 @@ func (cfg *Config) Statistics() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) forEachRoute(do func(alias string, r R.Route, p *PR.Provider)) {
|
||||
cfg.proxyProviders.RangeAll(func(_ string, p *PR.Provider) {
|
||||
p.RangeRoutes(func(a string, r R.Route) {
|
||||
do(a, r, p)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (cfg *Config) watchChanges() {
|
||||
cfg.watcherCtx, cfg.watcherCancel = context.WithCancel(context.Background())
|
||||
go func() {
|
||||
@@ -182,64 +173,82 @@ func (cfg *Config) watchChanges() {
|
||||
}()
|
||||
}
|
||||
|
||||
func (cfg *Config) load() E.NestedError {
|
||||
func (cfg *Config) load() (res E.NestedError) {
|
||||
b := E.NewBuilder("errors loading config")
|
||||
defer b.To(&res)
|
||||
|
||||
cfg.l.Debug("loading config")
|
||||
defer cfg.l.Debug("loaded config")
|
||||
|
||||
data, err := cfg.reader.Read()
|
||||
data, err := E.Check(os.ReadFile(common.ConfigPath))
|
||||
if err.HasError() {
|
||||
return E.Failure("read config").With(err)
|
||||
}
|
||||
|
||||
model := M.DefaultConfig()
|
||||
if err := E.From(yaml.Unmarshal(data, model)); err.HasError() {
|
||||
return E.Failure("parse config").With(err)
|
||||
b.Add(E.FailWith("read config", err))
|
||||
return
|
||||
}
|
||||
|
||||
if !common.NoSchemaValidation {
|
||||
if err = Validate(data); err.HasError() {
|
||||
return err
|
||||
b.Add(E.FailWith("schema validation", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
warnings := E.NewBuilder("errors loading config")
|
||||
model := M.DefaultConfig()
|
||||
if err := E.From(yaml.Unmarshal(data, model)); err.HasError() {
|
||||
b.Add(E.FailWith("parse config", err))
|
||||
return
|
||||
}
|
||||
|
||||
cfg.l.Debug("initializing autocert")
|
||||
ap, err := autocert.NewConfig(&model.AutoCert).GetProvider()
|
||||
if err.HasError() {
|
||||
warnings.Add(E.Failure("autocert provider").With(err))
|
||||
} else {
|
||||
cfg.l.Debug("initialized autocert")
|
||||
}
|
||||
cfg.autocertProvider = ap
|
||||
|
||||
cfg.l.Debug("loading providers")
|
||||
cfg.proxyProviders = F.NewMap[string, *PR.Provider]()
|
||||
for _, filename := range model.Providers.Files {
|
||||
p := PR.NewFileProvider(filename)
|
||||
cfg.proxyProviders.Set(p.GetName(), p)
|
||||
}
|
||||
for name, dockerHost := range model.Providers.Docker {
|
||||
p := PR.NewDockerProvider(name, dockerHost)
|
||||
cfg.proxyProviders.Set(p.GetName(), p)
|
||||
}
|
||||
cfg.l.Debug("loaded providers")
|
||||
// errors are non fatal below
|
||||
b.WithSeverity(E.SeverityWarning)
|
||||
b.Add(cfg.initAutoCert(&model.AutoCert))
|
||||
b.Add(cfg.loadProviders(&model.Providers))
|
||||
|
||||
cfg.value = model
|
||||
return
|
||||
}
|
||||
|
||||
if err := warnings.Build(); err.HasError() {
|
||||
cfg.l.Warn(err)
|
||||
func (cfg *Config) initAutoCert(autocertCfg *M.AutoCertConfig) (err E.NestedError) {
|
||||
if cfg.autocertProvider != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg.l.Debug("loaded config")
|
||||
return E.Nil()
|
||||
cfg.l.Debug("initializing autocert")
|
||||
defer cfg.l.Debug("initialized autocert")
|
||||
|
||||
cfg.autocertProvider, err = autocert.NewConfig(autocertCfg).GetProvider()
|
||||
if err.HasError() {
|
||||
err = E.FailWith("autocert provider", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *Config) loadProviders(providers *M.ProxyProviders) (res E.NestedError) {
|
||||
cfg.l.Debug("loading providers")
|
||||
defer cfg.l.Debug("loaded providers")
|
||||
|
||||
b := E.NewBuilder("errors loading providers")
|
||||
defer b.To(&res)
|
||||
|
||||
for _, filename := range providers.Files {
|
||||
p := PR.NewFileProvider(filename)
|
||||
cfg.proxyProviders.Store(p.GetName(), p)
|
||||
b.Add(p.LoadRoutes())
|
||||
}
|
||||
for name, dockerHost := range providers.Docker {
|
||||
p := PR.NewDockerProvider(name, dockerHost)
|
||||
cfg.proxyProviders.Store(p.GetName(), p)
|
||||
b.Add(p.LoadRoutes())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *Config) controlProviders(action string, do func(*PR.Provider) E.NestedError) {
|
||||
errors := E.NewBuilder("cannot %s these providers", action)
|
||||
|
||||
cfg.proxyProviders.EachKVParallel(func(name string, p *PR.Provider) {
|
||||
cfg.proxyProviders.RangeAll(func(name string, p *PR.Provider) {
|
||||
if err := do(p); err.HasError() {
|
||||
errors.Add(E.From(err).Subject(p))
|
||||
errors.Add(err.Subject(p))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user