v0.5.0-rc5: check release

This commit is contained in:
yusing
2024-09-19 20:40:03 +08:00
parent be7a766cb2
commit 4a2d42bfa9
68 changed files with 1971 additions and 1107 deletions

View File

@@ -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))
}
})