From b296fb29659f6e18bb3149cd583c0d0a20685d69 Mon Sep 17 00:00:00 2001 From: yusing Date: Sat, 19 Oct 2024 00:13:55 +0800 Subject: [PATCH] fixed healthcheck failed to disable and nil dereference --- internal/api/handler.go | 1 + internal/api/v1/list.go | 24 +++++++++++++++++++ internal/proxy/entry/entry.go | 2 +- internal/proxy/entry/raw.go | 16 ++++++------- internal/route/http.go | 8 +++++-- internal/route/provider/file.go | 2 ++ internal/route/stream.go | 4 ++-- internal/task/task.go | 6 ++++- internal/watcher/health/healthcheck_config.go | 2 +- internal/watcher/health/monitor.go | 13 ++++++---- schema/providers.schema.json | 2 +- 11 files changed, 60 insertions(+), 20 deletions(-) diff --git a/internal/api/handler.go b/internal/api/handler.go index 34429af..8f66864 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -31,6 +31,7 @@ func NewHandler() http.Handler { mux.HandleFunc("POST", "/v1/reload", v1.Reload) mux.HandleFunc("GET", "/v1/list", v1.List) mux.HandleFunc("GET", "/v1/list/{what}", v1.List) + mux.HandleFunc("GET", "/v1/list/{what}/{which}", v1.List) mux.HandleFunc("GET", "/v1/file", v1.GetFileContent) mux.HandleFunc("GET", "/v1/file/{filename...}", v1.GetFileContent) mux.HandleFunc("POST", "/v1/file/{filename...}", v1.SetFileContent) diff --git a/internal/api/v1/list.go b/internal/api/v1/list.go index 1b71452..fc1e8d8 100644 --- a/internal/api/v1/list.go +++ b/internal/api/v1/list.go @@ -14,6 +14,7 @@ import ( ) const ( + ListRoute = "route" ListRoutes = "routes" ListConfigFiles = "config_files" ListMiddlewares = "middlewares" @@ -28,8 +29,16 @@ func List(w http.ResponseWriter, r *http.Request) { if what == "" { what = ListRoutes } + which := r.PathValue("which") switch what { + case ListRoute: + if route := listRoute(which); route == nil { + http.Error(w, "not found", http.StatusNotFound) + return + } else { + U.RespondJSON(w, r, route) + } case ListRoutes: U.RespondJSON(w, r, config.RoutesByAlias(route.RouteType(r.FormValue("type")))) case ListConfigFiles: @@ -49,6 +58,21 @@ func List(w http.ResponseWriter, r *http.Request) { } } +func listRoute(which string) any { + if which == "" { + which = "all" + } + if which == "all" { + return config.RoutesByAlias() + } + routes := config.RoutesByAlias() + route, ok := routes[which] + if !ok { + return nil + } + return route +} + func listConfigFiles(w http.ResponseWriter, r *http.Request) { files, err := utils.ListFiles(common.ConfigBasePath, 1) if err != nil { diff --git a/internal/proxy/entry/entry.go b/internal/proxy/entry/entry.go index af69ba4..9e80ac3 100644 --- a/internal/proxy/entry/entry.go +++ b/internal/proxy/entry/entry.go @@ -64,5 +64,5 @@ func UseIdleWatcher(entry Entry) bool { func UseHealthCheck(entry Entry) bool { hc := entry.HealthCheckConfig() - return hc != nil && !hc.Disabled + return hc != nil && !hc.Disable } diff --git a/internal/proxy/entry/raw.go b/internal/proxy/entry/raw.go index 26f5161..6135618 100644 --- a/internal/proxy/entry/raw.go +++ b/internal/proxy/entry/raw.go @@ -126,15 +126,15 @@ func (e *RawEntry) FillMissingFields() { e.HealthCheck = new(health.HealthCheckConfig) } - if e.HealthCheck.Disabled { + if e.HealthCheck.Interval == 0 { + e.HealthCheck.Interval = common.HealthCheckIntervalDefault + } + if e.HealthCheck.Timeout == 0 { + e.HealthCheck.Timeout = common.HealthCheckTimeoutDefault + } + + if e.HealthCheck.Disable { e.HealthCheck = nil - } else { - if e.HealthCheck.Interval == 0 { - e.HealthCheck.Interval = common.HealthCheckIntervalDefault - } - if e.HealthCheck.Timeout == 0 { - e.HealthCheck.Timeout = common.HealthCheckTimeoutDefault - } } if cont.IdleTimeout != "" { diff --git a/internal/route/http.go b/internal/route/http.go index 19f57b0..f65dc5e 100755 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -106,9 +106,12 @@ func (r *HTTPRoute) Start(providerSubtask task.Task) E.NestedError { httpRoutesMu.Lock() defer httpRoutesMu.Unlock() - if r.HealthCheck.Disabled && (entry.UseLoadBalance(r) || entry.UseIdleWatcher(r)) { + if !entry.UseHealthCheck(r) && (entry.UseLoadBalance(r) || entry.UseIdleWatcher(r)) { logrus.Warnf("%s.healthCheck.disabled cannot be false when loadbalancer or idlewatcher is enabled", r.Alias) - r.HealthCheck.Disabled = true + if r.HealthCheck == nil { + r.HealthCheck = new(health.HealthCheckConfig) + } + r.HealthCheck.Disable = true } switch { @@ -121,6 +124,7 @@ func (r *HTTPRoute) Start(providerSubtask task.Task) E.NestedError { r.handler = waker r.HealthMon = waker case entry.UseHealthCheck(r): + logrus.Debugf("%s health check: %+v", r.Alias, r.HealthCheck) r.HealthMon = health.NewHTTPHealthMonitor(r.TargetURL(), r.HealthCheck, r.rp.Transport) } r.task = providerSubtask diff --git a/internal/route/provider/file.go b/internal/route/provider/file.go index 5fbc96a..c686b61 100644 --- a/internal/route/provider/file.go +++ b/internal/route/provider/file.go @@ -61,6 +61,8 @@ func (p *FileProvider) LoadRoutesImpl() (routes R.Routes, res E.NestedError) { return } + b.Add(Validate(data)) + return R.FromEntries(entries) } diff --git a/internal/route/stream.go b/internal/route/stream.go index 03b268c..1622a06 100755 --- a/internal/route/stream.go +++ b/internal/route/stream.go @@ -66,9 +66,9 @@ func (r *StreamRoute) Start(providerSubtask task.Task) E.NestedError { streamRoutesMu.Lock() defer streamRoutesMu.Unlock() - if r.HealthCheck.Disabled && (entry.UseLoadBalance(r) || entry.UseIdleWatcher(r)) { + if r.HealthCheck.Disable && (entry.UseLoadBalance(r) || entry.UseIdleWatcher(r)) { logrus.Warnf("%s.healthCheck.disabled cannot be false when loadbalancer or idlewatcher is enabled", r.Alias) - r.HealthCheck.Disabled = true + r.HealthCheck.Disable = true } if r.Scheme.ListeningScheme.IsTCP() { diff --git a/internal/task/task.go b/internal/task/task.go index 742f898..eb37106 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -160,7 +160,11 @@ func (t *task) Context() context.Context { } func (t *task) FinishCause() error { - return context.Cause(t.ctx) + cause := context.Cause(t.ctx) + if cause == nil { + return t.ctx.Err() + } + return cause } func (t *task) Parent() Task { diff --git a/internal/watcher/health/healthcheck_config.go b/internal/watcher/health/healthcheck_config.go index 293caa9..2857142 100644 --- a/internal/watcher/health/healthcheck_config.go +++ b/internal/watcher/health/healthcheck_config.go @@ -7,7 +7,7 @@ import ( ) type HealthCheckConfig struct { - Disabled bool `json:"disabled,omitempty" yaml:"disabled"` + Disable bool `json:"disable,omitempty" yaml:"disable"` Path string `json:"path,omitempty" yaml:"path"` UseGet bool `json:"use_get,omitempty" yaml:"use_get"` Interval time.Duration `json:"interval" yaml:"interval"` diff --git a/internal/watcher/health/monitor.go b/internal/watcher/health/monitor.go index 1890b6c..05405d1 100644 --- a/internal/watcher/health/monitor.go +++ b/internal/watcher/health/monitor.go @@ -75,20 +75,25 @@ func (mon *monitor) Start(routeSubtask task.Task) E.NestedError { mon.service = routeSubtask.Parent().Name() mon.task = routeSubtask - if err := mon.checkUpdateHealth(); err != nil { - mon.task.Finish(fmt.Sprintf("healthchecker %s failure: %s", mon.service, err)) - return err + if mon.config.Interval <= 0 { + return E.Invalid("interval", mon.config.Interval) } + go func() { defer func() { - monMap.Delete(mon.task.Name()) if mon.status.Load() != StatusError { mon.status.Store(StatusUnknown) } mon.task.Finish(mon.task.FinishCause().Error()) }() + if err := mon.checkUpdateHealth(); err != nil { + logger.Errorf("healthchecker %s failure: %s", mon.service, err) + return + } + monMap.Store(mon.service, mon) + defer monMap.Delete(mon.service) ticker := time.NewTicker(mon.config.Interval) defer ticker.Stop() diff --git a/schema/providers.schema.json b/schema/providers.schema.json index 8a23e7e..fa458ee 100644 --- a/schema/providers.schema.json +++ b/schema/providers.schema.json @@ -149,7 +149,7 @@ "healthcheck": { "type": "object", "properties": { - "disabled": { + "disable": { "type": "boolean", "default": false },