mirror of
https://github.com/yusing/godoxy.git
synced 2026-01-11 22:30:47 +01:00
refactor(healthcheck): streamline health check configuration and defaults
- Moved health check constants from common package alongside type definition. - Updated health check configuration to use struct directly instead of pointers. - Introduced global default health check config
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// file, folder structure
|
||||
|
||||
const (
|
||||
@@ -38,10 +34,6 @@ var RequiredDirectories = []string{
|
||||
const DockerHostFromEnv = "$DOCKER_HOST"
|
||||
|
||||
const (
|
||||
HealthCheckIntervalDefault = 5 * time.Second
|
||||
HealthCheckTimeoutDefault = 5 * time.Second
|
||||
HealthCheckDownNotifyDelayDefault = 15 * time.Second
|
||||
|
||||
WakeTimeoutDefault = "3m"
|
||||
StopTimeoutDefault = "3m"
|
||||
StopMethodDefault = "stop"
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/proxmox"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
@@ -25,8 +26,12 @@ type (
|
||||
Providers Providers `json:"providers"`
|
||||
MatchDomains []string `json:"match_domains" validate:"domain_name"`
|
||||
Homepage homepage.Config `json:"homepage"`
|
||||
Defaults Defaults `json:"defaults"`
|
||||
TimeoutShutdown int `json:"timeout_shutdown" validate:"gte=0"`
|
||||
}
|
||||
Defaults struct {
|
||||
HealthCheck types.HealthCheckConfig `json:"healthcheck"`
|
||||
}
|
||||
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"`
|
||||
|
||||
@@ -82,7 +82,7 @@ func BenchmarkEntrypointReal(b *testing.B) {
|
||||
Scheme: routeTypes.SchemeHTTP,
|
||||
Host: host,
|
||||
Port: route.Port{Proxy: portInt},
|
||||
HealthCheck: &types.HealthCheckConfig{Disable: true},
|
||||
HealthCheck: types.HealthCheckConfig{Disable: true},
|
||||
}
|
||||
|
||||
err = r.Validate()
|
||||
@@ -125,7 +125,7 @@ func BenchmarkEntrypoint(b *testing.B) {
|
||||
Port: route.Port{
|
||||
Proxy: 8080,
|
||||
},
|
||||
HealthCheck: &types.HealthCheckConfig{
|
||||
HealthCheck: types.HealthCheckConfig{
|
||||
Disable: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
gphttp "github.com/yusing/godoxy/internal/net/gphttp"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/homepage"
|
||||
homepagecfg "github.com/yusing/godoxy/internal/homepage/types"
|
||||
@@ -49,7 +50,7 @@ type (
|
||||
PathPatterns []string `json:"path_patterns,omitempty" extensions:"x-nullable"`
|
||||
Rules rules.Rules `json:"rules,omitempty" extension:"x-nullable"`
|
||||
RuleFile string `json:"rule_file,omitempty" extensions:"x-nullable"`
|
||||
HealthCheck *types.HealthCheckConfig `json:"healthcheck,omitempty" extensions:"x-nullable"` // null on load-balancer routes
|
||||
HealthCheck types.HealthCheckConfig `json:"healthcheck,omitempty" extensions:"x-nullable"` // null on load-balancer routes
|
||||
LoadBalance *types.LoadBalancerConfig `json:"load_balance,omitempty" extensions:"x-nullable"`
|
||||
Middlewares map[string]types.LabelMap `json:"middlewares,omitempty" extensions:"x-nullable"`
|
||||
Homepage *homepage.ItemConfig `json:"homepage"`
|
||||
@@ -497,7 +498,7 @@ func (r *Route) IdlewatcherConfig() *types.IdlewatcherConfig {
|
||||
return r.Idlewatcher
|
||||
}
|
||||
|
||||
func (r *Route) HealthCheckConfig() *types.HealthCheckConfig {
|
||||
func (r *Route) HealthCheckConfig() types.HealthCheckConfig {
|
||||
return r.HealthCheck
|
||||
}
|
||||
|
||||
@@ -771,17 +772,7 @@ func (r *Route) Finalize() {
|
||||
}
|
||||
|
||||
r.Port.Listening, r.Port.Proxy = lp, pp
|
||||
|
||||
if r.HealthCheck == nil {
|
||||
r.HealthCheck = types.DefaultHealthConfig()
|
||||
}
|
||||
|
||||
if r.HealthCheck.Interval == 0 {
|
||||
r.HealthCheck.Interval = common.HealthCheckIntervalDefault
|
||||
}
|
||||
if r.HealthCheck.Timeout == 0 {
|
||||
r.HealthCheck.Timeout = common.HealthCheckTimeoutDefault
|
||||
}
|
||||
r.HealthCheck.ApplyDefaults(config.ActiveConfig.Load().Defaults.HealthCheck)
|
||||
}
|
||||
|
||||
func (r *Route) FinalizeHomepageConfig() {
|
||||
@@ -794,7 +785,6 @@ func (r *Route) FinalizeHomepageConfig() {
|
||||
if r.Homepage == nil {
|
||||
r.Homepage = &homepage.ItemConfig{
|
||||
Show: true,
|
||||
Name: r.Alias,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package route
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
route "github.com/yusing/godoxy/internal/route/types"
|
||||
@@ -40,7 +41,7 @@ func TestRouteValidate(t *testing.T) {
|
||||
Scheme: route.SchemeHTTP,
|
||||
Host: "example.com",
|
||||
Port: route.Port{Proxy: 80},
|
||||
HealthCheck: &types.HealthCheckConfig{
|
||||
HealthCheck: types.HealthCheckConfig{
|
||||
Disable: true,
|
||||
},
|
||||
LoadBalance: &types.LoadBalancerConfig{
|
||||
@@ -179,3 +180,14 @@ func TestRouteAgent(t *testing.T) {
|
||||
expect.NoError(t, err, "Validate should not return error for valid route with agent")
|
||||
expect.NotNil(t, r.GetAgent(), "GetAgent should return agent")
|
||||
}
|
||||
|
||||
func TestRouteApplyingHealthCheckDefaults(t *testing.T) {
|
||||
hc := types.HealthCheckConfig{}
|
||||
hc.ApplyDefaults(types.HealthCheckConfig{
|
||||
Interval: 15 * time.Second,
|
||||
Timeout: 10 * time.Second,
|
||||
})
|
||||
|
||||
expect.Equal(t, hc.Interval, 15*time.Second)
|
||||
expect.Equal(t, hc.Timeout, 10*time.Second)
|
||||
}
|
||||
|
||||
@@ -3,25 +3,39 @@ package types
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
)
|
||||
|
||||
type HealthCheckConfig struct {
|
||||
Disable bool `json:"disable,omitempty" aliases:"disabled"`
|
||||
Path string `json:"path,omitempty" validate:"omitempty,uri,startswith=/"`
|
||||
UseGet bool `json:"use_get,omitempty"`
|
||||
Path string `json:"path,omitempty" validate:"omitempty,uri,startswith=/"`
|
||||
Interval time.Duration `json:"interval" validate:"omitempty,min=1s" swaggertype:"primitive,integer"`
|
||||
Timeout time.Duration `json:"timeout" validate:"omitempty,min=1s" swaggertype:"primitive,integer"`
|
||||
Retries int64 `json:"retries"` // <0: immediate, >=0: threshold
|
||||
Retries int64 `json:"retries"` // <0: immediate, 0: default, >0: threshold
|
||||
|
||||
BaseContext func() context.Context `json:"-"`
|
||||
} // @name HealthCheckConfig
|
||||
|
||||
func DefaultHealthConfig() *HealthCheckConfig {
|
||||
return &HealthCheckConfig{
|
||||
Interval: common.HealthCheckIntervalDefault,
|
||||
Timeout: common.HealthCheckTimeoutDefault,
|
||||
Retries: int64(common.HealthCheckDownNotifyDelayDefault / common.HealthCheckIntervalDefault),
|
||||
const (
|
||||
HealthCheckIntervalDefault = 5 * time.Second
|
||||
HealthCheckTimeoutDefault = 5 * time.Second
|
||||
HealthCheckDownNotifyDelayDefault = 15 * time.Second
|
||||
)
|
||||
|
||||
func (hc *HealthCheckConfig) ApplyDefaults(defaults HealthCheckConfig) {
|
||||
if hc.Interval == 0 {
|
||||
hc.Interval = defaults.Interval
|
||||
if hc.Interval == 0 {
|
||||
hc.Interval = HealthCheckIntervalDefault
|
||||
}
|
||||
}
|
||||
if hc.Timeout == 0 {
|
||||
hc.Timeout = defaults.Timeout
|
||||
if hc.Timeout == 0 {
|
||||
hc.Timeout = HealthCheckTimeoutDefault
|
||||
}
|
||||
}
|
||||
if hc.Retries == 0 {
|
||||
hc.Retries = int64(HealthCheckDownNotifyDelayDefault / hc.Interval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ type (
|
||||
Started() <-chan struct{}
|
||||
|
||||
IdlewatcherConfig() *IdlewatcherConfig
|
||||
HealthCheckConfig() *HealthCheckConfig
|
||||
HealthCheckConfig() HealthCheckConfig
|
||||
LoadBalanceConfig() *LoadBalancerConfig
|
||||
HomepageItem() homepage.Item
|
||||
DisplayName() string
|
||||
|
||||
@@ -45,7 +45,7 @@ func (target *AgentCheckHealthTarget) displayURL() *url.URL {
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config *types.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
||||
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config types.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
||||
mon := &AgentProxiedMonitor{
|
||||
agent: agent,
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type DockerHealthMonitor struct {
|
||||
|
||||
const dockerFailuresThreshold = 3
|
||||
|
||||
func NewDockerHealthMonitor(client *docker.SharedClient, containerID, alias string, config *types.HealthCheckConfig, fallback types.HealthChecker) *DockerHealthMonitor {
|
||||
func NewDockerHealthMonitor(client *docker.SharedClient, containerID, alias string, config types.HealthCheckConfig, fallback types.HealthChecker) *DockerHealthMonitor {
|
||||
mon := new(DockerHealthMonitor)
|
||||
mon.client = client
|
||||
mon.containerID = containerID
|
||||
|
||||
@@ -12,7 +12,7 @@ type FileServerHealthMonitor struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func NewFileServerHealthMonitor(config *types.HealthCheckConfig, path string) *FileServerHealthMonitor {
|
||||
func NewFileServerHealthMonitor(config types.HealthCheckConfig, path string) *FileServerHealthMonitor {
|
||||
mon := &FileServerHealthMonitor{path: path}
|
||||
mon.monitor = newMonitor(nil, config, mon.CheckHealth)
|
||||
return mon
|
||||
|
||||
@@ -29,7 +29,7 @@ var pinger = &fasthttp.Client{
|
||||
NoDefaultUserAgentHeader: true,
|
||||
}
|
||||
|
||||
func NewHTTPHealthMonitor(url *url.URL, config *types.HealthCheckConfig) *HTTPHealthMonitor {
|
||||
func NewHTTPHealthMonitor(url *url.URL, config types.HealthCheckConfig) *HTTPHealthMonitor {
|
||||
mon := new(HTTPHealthMonitor)
|
||||
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
||||
if config.UseGet {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
@@ -24,7 +24,7 @@ type (
|
||||
HealthCheckFunc func() (result types.HealthCheckResult, err error)
|
||||
monitor struct {
|
||||
service string
|
||||
config *types.HealthCheckConfig
|
||||
config types.HealthCheckConfig
|
||||
url synk.Value[*url.URL]
|
||||
|
||||
status synk.Value[types.HealthStatus]
|
||||
@@ -73,6 +73,7 @@ func NewMonitor(r types.Route) types.HealthMonCheck {
|
||||
return mon
|
||||
}
|
||||
|
||||
func newMonitor(u *url.URL, cfg types.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
|
||||
cfg.ApplyDefaults(config.DefaultConfig().Defaults.HealthCheck)
|
||||
mon := &monitor{
|
||||
config: cfg,
|
||||
@@ -196,7 +197,7 @@ func (mon *monitor) URL() *url.URL {
|
||||
|
||||
// Config implements HealthChecker.
|
||||
func (mon *monitor) Config() *types.HealthCheckConfig {
|
||||
return mon.config
|
||||
return &mon.config
|
||||
}
|
||||
|
||||
// Status implements HealthMonitor.
|
||||
@@ -237,7 +238,7 @@ func (mon *monitor) MarshalJSON() ([]byte, error) {
|
||||
res := mon.lastResult.Load()
|
||||
return (&types.HealthJSONRepr{
|
||||
Name: mon.service,
|
||||
Config: mon.config,
|
||||
Config: &mon.config,
|
||||
Status: mon.status.Load(),
|
||||
Started: mon.startTime,
|
||||
Uptime: mon.Uptime(),
|
||||
|
||||
@@ -28,7 +28,7 @@ func (t *testNotificationTracker) getStats() (up, down int, last string) {
|
||||
}
|
||||
|
||||
// Create test monitor with mock health checker - returns both monitor and tracker
|
||||
func createTestMonitor(config *types.HealthCheckConfig, checkFunc HealthCheckFunc) (*monitor, *testNotificationTracker) {
|
||||
func createTestMonitor(config types.HealthCheckConfig, checkFunc HealthCheckFunc) (*monitor, *testNotificationTracker) {
|
||||
testURL, _ := url.Parse("http://localhost:8080")
|
||||
|
||||
mon := newMonitor(testURL, config, checkFunc)
|
||||
@@ -56,7 +56,7 @@ func createTestMonitor(config *types.HealthCheckConfig, checkFunc HealthCheckFun
|
||||
}
|
||||
|
||||
func TestNotification_ImmediateNotifyAfterZero(t *testing.T) {
|
||||
config := &types.HealthCheckConfig{
|
||||
config := types.HealthCheckConfig{
|
||||
Interval: 100 * time.Millisecond,
|
||||
Timeout: 50 * time.Millisecond,
|
||||
Retries: -1, // Immediate notification
|
||||
@@ -91,7 +91,7 @@ func TestNotification_ImmediateNotifyAfterZero(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotification_WithNotifyAfterThreshold(t *testing.T) {
|
||||
config := &types.HealthCheckConfig{
|
||||
config := types.HealthCheckConfig{
|
||||
Interval: 50 * time.Millisecond,
|
||||
Timeout: 50 * time.Millisecond,
|
||||
Retries: 2, // Notify after 2 consecutive failures
|
||||
@@ -130,7 +130,7 @@ func TestNotification_WithNotifyAfterThreshold(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotification_ServiceRecoversBeforeThreshold(t *testing.T) {
|
||||
config := &types.HealthCheckConfig{
|
||||
config := types.HealthCheckConfig{
|
||||
Interval: 100 * time.Millisecond,
|
||||
Timeout: 50 * time.Millisecond,
|
||||
Retries: 3, // Notify after 3 consecutive failures
|
||||
@@ -179,7 +179,7 @@ func TestNotification_ServiceRecoversBeforeThreshold(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotification_ConsecutiveFailureReset(t *testing.T) {
|
||||
config := &types.HealthCheckConfig{
|
||||
config := types.HealthCheckConfig{
|
||||
Interval: 100 * time.Millisecond,
|
||||
Timeout: 50 * time.Millisecond,
|
||||
Retries: 2, // Notify after 2 consecutive failures
|
||||
@@ -240,7 +240,7 @@ func TestNotification_ConsecutiveFailureReset(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotification_ContextCancellation(t *testing.T) {
|
||||
config := &types.HealthCheckConfig{
|
||||
config := types.HealthCheckConfig{
|
||||
Interval: 100 * time.Millisecond,
|
||||
Timeout: 50 * time.Millisecond,
|
||||
Retries: 1,
|
||||
@@ -279,7 +279,7 @@ func TestNotification_ContextCancellation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImmediateUpNotification(t *testing.T) {
|
||||
config := &types.HealthCheckConfig{
|
||||
config := types.HealthCheckConfig{
|
||||
Interval: 100 * time.Millisecond,
|
||||
Timeout: 50 * time.Millisecond,
|
||||
Retries: 2, // NotifyAfter should not affect up notifications
|
||||
|
||||
@@ -17,7 +17,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func NewRawHealthMonitor(url *url.URL, config *types.HealthCheckConfig) *RawHealthMonitor {
|
||||
func NewRawHealthMonitor(url *url.URL, config types.HealthCheckConfig) *RawHealthMonitor {
|
||||
mon := new(RawHealthMonitor)
|
||||
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
||||
mon.dialer = &net.Dialer{
|
||||
|
||||
Reference in New Issue
Block a user