refactor(config): enhance provider conflict error messages, streamline provider loading, and improve validation for config files, rename provider.GetType() to Type()

This commit is contained in:
yusing 2025-04-05 13:30:24 +08:00
parent 73a5c57d67
commit b53dd17b84
4 changed files with 35 additions and 47 deletions

View file

@ -51,6 +51,8 @@ You may run "ls-config" to show or dump the current config.`
var Validate = config.Validate
var ErrProviderNameConflict = gperr.New("provider name conflict")
func newConfig() *Config {
return &Config{
value: config.DefaultConfig(),
@ -263,56 +265,48 @@ func (cfg *Config) initAutoCert(autocertCfg *autocert.AutocertConfig) (err gperr
}
func (cfg *Config) errIfExists(p *proxy.Provider) gperr.Error {
if _, ok := cfg.providers.Load(p.String()); ok {
return gperr.Errorf("provider %s already exists", p.String())
if conflict, ok := cfg.providers.Load(p.String()); ok {
return ErrProviderNameConflict.
Subject(p.String()).
Withf("one is %q", conflict.Type()).
Withf("the other is %q", p.Type())
}
return nil
}
func (cfg *Config) storeProvider(p *proxy.Provider) {
cfg.providers.Store(p.String(), p)
}
func (cfg *Config) loadRouteProviders(providers *config.Providers) gperr.Error {
errs := gperr.NewBuilder("route provider errors")
results := gperr.NewBuilder("loaded route providers")
removeAllAgents()
routeProviders := make([]*proxy.Provider, 0, len(providers.Agents)+len(providers.Docker)+len(providers.Files))
for _, agent := range providers.Agents {
if err := agent.Init(cfg.task.Context()); err != nil {
errs.Add(err.Subject(agent.String()))
continue
}
addAgent(agent)
p := proxy.NewAgentProvider(agent)
if err := cfg.errIfExists(p); err != nil {
errs.Add(err.Subject(p.String()))
continue
}
cfg.storeProvider(p)
routeProviders = append(routeProviders, proxy.NewAgentProvider(agent))
}
for _, filename := range providers.Files {
p, err := proxy.NewFileProvider(filename)
if err == nil {
err = cfg.errIfExists(p)
}
if err != nil {
errs.Add(gperr.PrependSubject(filename, err))
continue
}
cfg.storeProvider(p)
routeProviders = append(routeProviders, proxy.NewFileProvider(filename))
}
for name, dockerHost := range providers.Docker {
p := proxy.NewDockerProvider(name, dockerHost)
routeProviders = append(routeProviders, proxy.NewDockerProvider(name, dockerHost))
}
if len(routeProviders) == 0 {
return nil
}
// check if all providers are unique (should not happen but just in case)
for _, p := range routeProviders {
if err := cfg.errIfExists(p); err != nil {
errs.Add(err.Subject(p.String()))
errs.Add(err)
continue
}
cfg.storeProvider(p)
}
if cfg.providers.Size() == 0 {
return nil
cfg.providers.Store(p.String(), p)
}
lenLongestName := 0

View file

@ -2,12 +2,15 @@ package types
import (
"context"
"os"
"path"
"regexp"
"sync"
"github.com/go-playground/validator/v10"
"github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/autocert"
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
"github.com/yusing/go-proxy/internal/notif"
@ -24,10 +27,10 @@ type (
TimeoutShutdown int `json:"timeout_shutdown" validate:"gte=0"`
}
Providers struct {
Files []string `json:"include" yaml:"include,omitempty" validate:"dive,filepath"`
Docker map[string]string `json:"docker" yaml:"docker,omitempty" validate:"non_empty_docker_keys,dive,unix_addr|url"`
Agents []*agent.AgentConfig `json:"agents" yaml:"agents,omitempty"`
Notification []notif.NotificationConfig `json:"notification" yaml:"notification,omitempty"`
Files []string `json:"include" yaml:"include,omitempty" validate:"unique,dive,config_file_exists"`
Docker map[string]string `json:"docker" yaml:"docker,omitempty" validate:"unique,dive,unix_addr|url"`
Agents []*agent.AgentConfig `json:"agents" yaml:"agents,omitempty" validate:"unique=Addr"`
Notification []notif.NotificationConfig `json:"notification" yaml:"notification,omitempty" validate:"unique=ProviderName"`
}
Entrypoint struct {
Middlewares []map[string]any `json:"middlewares"`
@ -97,13 +100,9 @@ func init() {
}
return true
})
utils.MustRegisterValidation("non_empty_docker_keys", func(fl validator.FieldLevel) bool {
m := fl.Field().Interface().(map[string]string)
for k := range m {
if k == "" {
return false
}
}
return true
utils.MustRegisterValidation("config_file_exists", func(fl validator.FieldLevel) bool {
filename := fl.Field().Interface().(string)
info, err := os.Stat(path.Join(common.ConfigBasePath, filename))
return err == nil && !info.IsDir()
})
}

View file

@ -73,7 +73,7 @@ func (handler *EventHandler) matchAny(events []watcher.Event, route *route.Route
}
func (handler *EventHandler) match(event watcher.Event, route *route.Route) bool {
switch handler.provider.GetType() {
switch handler.provider.Type() {
case types.ProviderTypeDocker, types.ProviderTypeAgent:
return route.Container.ContainerID == event.ActorID ||
route.Container.ContainerName == event.ActorName

View file

@ -20,17 +20,12 @@ type FileProvider struct {
l zerolog.Logger
}
func FileProviderImpl(filename string) (ProviderImpl, error) {
impl := &FileProvider{
func FileProviderImpl(filename string) ProviderImpl {
return &FileProvider{
fileName: filename,
path: path.Join(common.ConfigBasePath, filename),
l: logging.With().Str("type", "file").Str("name", filename).Logger(),
}
_, err := os.Stat(impl.path)
if err != nil {
return nil, err
}
return impl, nil
}
func validate(data []byte) (routes route.Routes, err gperr.Error) {