mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-25 10:18:59 +02:00
merge: main branch
This commit is contained in:
@@ -36,8 +36,8 @@ const (
|
||||
|
||||
ActionForceReload
|
||||
|
||||
actionContainerWakeMask = ActionContainerCreate | ActionContainerStart | ActionContainerUnpause
|
||||
actionContainerSleepMask = ActionContainerKill | ActionContainerStop | ActionContainerPause | ActionContainerDie
|
||||
actionContainerStartMask = ActionContainerCreate | ActionContainerStart | ActionContainerUnpause
|
||||
actionContainerStopMask = ActionContainerKill | ActionContainerStop | ActionContainerDie
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -83,10 +83,14 @@ func (a Action) String() string {
|
||||
return actionNameMap[a]
|
||||
}
|
||||
|
||||
func (a Action) IsContainerWake() bool {
|
||||
return a&actionContainerWakeMask != 0
|
||||
func (a Action) IsContainerStart() bool {
|
||||
return a&actionContainerStartMask != 0
|
||||
}
|
||||
|
||||
func (a Action) IsContainerSleep() bool {
|
||||
return a&actionContainerSleepMask != 0
|
||||
func (a Action) IsContainerStop() bool {
|
||||
return a&actionContainerStopMask != 0
|
||||
}
|
||||
|
||||
func (a Action) IsContainerPause() bool {
|
||||
return a == ActionContainerPause
|
||||
}
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
package monitor
|
||||
package health
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
type JSONRepresentation struct {
|
||||
Name string
|
||||
Config *health.HealthCheckConfig
|
||||
Status health.Status
|
||||
Config *HealthCheckConfig
|
||||
Status Status
|
||||
Started time.Time
|
||||
Uptime time.Duration
|
||||
Latency time.Duration
|
||||
LastSeen time.Time
|
||||
Detail string
|
||||
URL *net.URL
|
||||
URL *url.URL
|
||||
Extra map[string]any
|
||||
}
|
||||
|
||||
func (jsonRepr *JSONRepresentation) MarshalJSON() ([]byte, error) {
|
||||
url := jsonRepr.URL.String()
|
||||
func (jsonRepr *JSONRepresentation) MarshalMap() map[string]any {
|
||||
var url string
|
||||
if jsonRepr.URL != nil {
|
||||
url = jsonRepr.URL.String()
|
||||
}
|
||||
if url == "http://:0" {
|
||||
url = ""
|
||||
}
|
||||
return json.Marshal(map[string]any{
|
||||
return map[string]any{
|
||||
"name": jsonRepr.Name,
|
||||
"config": jsonRepr.Config,
|
||||
"started": jsonRepr.Started.Unix(),
|
||||
@@ -43,5 +44,5 @@ func (jsonRepr *JSONRepresentation) MarshalJSON() ([]byte, error) {
|
||||
"detail": jsonRepr.Detail,
|
||||
"url": url,
|
||||
"extra": jsonRepr.Extra,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/url"
|
||||
|
||||
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
@@ -24,7 +23,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func AgentTargetFromURL(url *types.URL) *AgentCheckHealthTarget {
|
||||
func AgentTargetFromURL(url *url.URL) *AgentCheckHealthTarget {
|
||||
return &AgentCheckHealthTarget{
|
||||
Scheme: url.Scheme,
|
||||
Host: url.Host,
|
||||
@@ -40,12 +39,12 @@ func (target *AgentCheckHealthTarget) buildQuery() string {
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
func (target *AgentCheckHealthTarget) displayURL() *types.URL {
|
||||
return types.NewURL(&url.URL{
|
||||
func (target *AgentCheckHealthTarget) displayURL() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: target.Scheme,
|
||||
Host: target.Host,
|
||||
Path: target.Path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config *health.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
@@ -25,7 +25,9 @@ func NewDockerHealthMonitor(client *docker.SharedClient, containerID, alias stri
|
||||
}
|
||||
|
||||
func (mon *DockerHealthMonitor) CheckHealth() (result *health.HealthCheckResult, err error) {
|
||||
cont, err := mon.client.ContainerInspect(mon.task.Context(), mon.containerID)
|
||||
ctx, cancel := mon.ContextWithTimeout("docker health check timed out")
|
||||
defer cancel()
|
||||
cont, err := mon.client.ContainerInspect(ctx, mon.containerID)
|
||||
if err != nil {
|
||||
return mon.fallback.CheckHealth()
|
||||
}
|
||||
@@ -46,7 +48,7 @@ func (mon *DockerHealthMonitor) CheckHealth() (result *health.HealthCheckResult,
|
||||
return mon.fallback.CheckHealth()
|
||||
}
|
||||
result = new(health.HealthCheckResult)
|
||||
result.Healthy = cont.State.Health.Status == dockerTypes.Healthy
|
||||
result.Healthy = cont.State.Health.Status == container.Healthy
|
||||
if len(cont.State.Health.Log) > 0 {
|
||||
lastLog := cont.State.Health.Log[len(cont.State.Health.Log)-1]
|
||||
result.Detail = lastLog.Output
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
)
|
||||
@@ -26,7 +26,7 @@ var pinger = &http.Client{
|
||||
},
|
||||
}
|
||||
|
||||
func NewHTTPHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *HTTPHealthMonitor {
|
||||
func NewHTTPHealthMonitor(url *url.URL, config *health.HealthCheckConfig) *HTTPHealthMonitor {
|
||||
mon := new(HTTPHealthMonitor)
|
||||
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
||||
if config.UseGet {
|
||||
@@ -37,10 +37,6 @@ func NewHTTPHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *HTT
|
||||
return mon
|
||||
}
|
||||
|
||||
func NewHTTPHealthChecker(url *types.URL, config *health.HealthCheckConfig) health.HealthChecker {
|
||||
return NewHTTPHealthMonitor(url, config)
|
||||
}
|
||||
|
||||
func (mon *HTTPHealthMonitor) CheckHealth() (result *health.HealthCheckResult, err error) {
|
||||
ctx, cancel := mon.ContextWithTimeout("ping request timed out")
|
||||
defer cancel()
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/metrics"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/notif"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/atomic"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
@@ -22,7 +23,7 @@ type (
|
||||
monitor struct {
|
||||
service string
|
||||
config *health.HealthCheckConfig
|
||||
url atomic.Value[*types.URL]
|
||||
url atomic.Value[*url.URL]
|
||||
|
||||
status atomic.Value[health.Status]
|
||||
lastResult atomic.Value[*health.HealthCheckResult]
|
||||
@@ -30,15 +31,39 @@ type (
|
||||
checkHealth HealthCheckFunc
|
||||
startTime time.Time
|
||||
|
||||
metric *metrics.Gauge
|
||||
|
||||
task *task.Task
|
||||
}
|
||||
)
|
||||
|
||||
var ErrNegativeInterval = errors.New("negative interval")
|
||||
|
||||
func newMonitor(url *types.URL, config *health.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
|
||||
func NewMonitor(r routes.Route) health.HealthMonCheck {
|
||||
var mon health.HealthMonCheck
|
||||
if r.IsAgent() {
|
||||
mon = NewAgentProxiedMonitor(r.Agent(), r.HealthCheckConfig(), AgentTargetFromURL(&r.TargetURL().URL))
|
||||
} else {
|
||||
switch r := r.(type) {
|
||||
case routes.HTTPRoute:
|
||||
mon = NewHTTPHealthMonitor(&r.TargetURL().URL, r.HealthCheckConfig())
|
||||
case routes.StreamRoute:
|
||||
mon = NewRawHealthMonitor(&r.TargetURL().URL, r.HealthCheckConfig())
|
||||
default:
|
||||
logging.Panic().Msgf("unexpected route type: %T", r)
|
||||
}
|
||||
}
|
||||
if r.IsDocker() {
|
||||
cont := r.ContainerInfo()
|
||||
client, err := docker.NewClient(cont.DockerHost)
|
||||
if err != nil {
|
||||
return mon
|
||||
}
|
||||
r.Task().OnCancel("close_docker_client", client.Close)
|
||||
return NewDockerHealthMonitor(client, cont.ContainerID, r.Name(), r.HealthCheckConfig(), mon)
|
||||
}
|
||||
return mon
|
||||
}
|
||||
|
||||
func newMonitor(url *url.URL, config *health.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
|
||||
mon := &monitor{
|
||||
config: config,
|
||||
checkHealth: healthCheckFunc,
|
||||
@@ -63,7 +88,7 @@ func (mon *monitor) Start(parent task.Parent) gperr.Error {
|
||||
}
|
||||
|
||||
mon.service = parent.Name()
|
||||
mon.task = parent.Subtask("health_monitor")
|
||||
mon.task = parent.Subtask("health_monitor", true)
|
||||
|
||||
go func() {
|
||||
logger := logging.With().Str("name", mon.service).Logger()
|
||||
@@ -72,9 +97,6 @@ func (mon *monitor) Start(parent task.Parent) gperr.Error {
|
||||
if mon.status.Load() != health.StatusError {
|
||||
mon.status.Store(health.StatusUnknown)
|
||||
}
|
||||
if mon.metric != nil {
|
||||
mon.metric.Reset()
|
||||
}
|
||||
mon.task.Finish(nil)
|
||||
}()
|
||||
|
||||
@@ -113,12 +135,12 @@ func (mon *monitor) Finish(reason any) {
|
||||
}
|
||||
|
||||
// UpdateURL implements HealthChecker.
|
||||
func (mon *monitor) UpdateURL(url *types.URL) {
|
||||
func (mon *monitor) UpdateURL(url *url.URL) {
|
||||
mon.url.Store(url)
|
||||
}
|
||||
|
||||
// URL implements HealthChecker.
|
||||
func (mon *monitor) URL() *types.URL {
|
||||
func (mon *monitor) URL() *url.URL {
|
||||
return mon.url.Load()
|
||||
}
|
||||
|
||||
@@ -157,8 +179,8 @@ func (mon *monitor) String() string {
|
||||
return mon.Name()
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler of HealthMonitor.
|
||||
func (mon *monitor) MarshalJSON() ([]byte, error) {
|
||||
// MarshalMap implements health.HealthMonitor.
|
||||
func (mon *monitor) MarshalMap() map[string]any {
|
||||
res := mon.lastResult.Load()
|
||||
if res == nil {
|
||||
res = &health.HealthCheckResult{
|
||||
@@ -166,7 +188,7 @@ func (mon *monitor) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return (&JSONRepresentation{
|
||||
return (&health.JSONRepresentation{
|
||||
Name: mon.service,
|
||||
Config: mon.config,
|
||||
Status: mon.status.Load(),
|
||||
@@ -176,7 +198,7 @@ func (mon *monitor) MarshalJSON() ([]byte, error) {
|
||||
LastSeen: GetLastSeen(mon.service),
|
||||
Detail: res.Detail,
|
||||
URL: mon.url.Load(),
|
||||
}).MarshalJSON()
|
||||
}).MarshalMap()
|
||||
}
|
||||
|
||||
func (mon *monitor) checkUpdateHealth() error {
|
||||
@@ -230,9 +252,6 @@ func (mon *monitor) checkUpdateHealth() error {
|
||||
})
|
||||
}
|
||||
}
|
||||
if mon.metric != nil {
|
||||
mon.metric.Set(float64(status))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package monitor
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func NewRawHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *RawHealthMonitor {
|
||||
func NewRawHealthMonitor(url *url.URL, config *health.HealthCheckConfig) *RawHealthMonitor {
|
||||
mon := new(RawHealthMonitor)
|
||||
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
||||
mon.dialer = &net.Dialer{
|
||||
@@ -25,10 +25,6 @@ func NewRawHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *RawH
|
||||
return mon
|
||||
}
|
||||
|
||||
func NewRawHealthChecker(url *types.URL, config *health.HealthCheckConfig) health.HealthChecker {
|
||||
return NewRawHealthMonitor(url, config)
|
||||
}
|
||||
|
||||
func (mon *RawHealthMonitor) CheckHealth() (result *health.HealthCheckResult, err error) {
|
||||
ctx, cancel := mon.ContextWithTimeout("ping request timed out")
|
||||
defer cancel()
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package health
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Status uint8
|
||||
|
||||
const (
|
||||
@@ -35,32 +33,6 @@ func (s Status) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (s Status) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + s.String() + `"`), nil
|
||||
}
|
||||
|
||||
func (s *Status) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
switch str {
|
||||
case "healthy":
|
||||
*s = StatusHealthy
|
||||
case "unhealthy":
|
||||
*s = StatusUnhealthy
|
||||
case "napping":
|
||||
*s = StatusNapping
|
||||
case "starting":
|
||||
*s = StatusStarting
|
||||
case "error":
|
||||
*s = StatusError
|
||||
default:
|
||||
*s = StatusUnknown
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Status) Good() bool {
|
||||
return s&HealthyMask != 0
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
)
|
||||
|
||||
@@ -24,14 +23,17 @@ type (
|
||||
task.TaskStarter
|
||||
task.TaskFinisher
|
||||
fmt.Stringer
|
||||
json.Marshaler
|
||||
WithHealthInfo
|
||||
Name() string
|
||||
}
|
||||
HealthChecker interface {
|
||||
CheckHealth() (result *HealthCheckResult, err error)
|
||||
URL() *types.URL
|
||||
URL() *url.URL
|
||||
Config() *HealthCheckConfig
|
||||
UpdateURL(url *types.URL)
|
||||
UpdateURL(url *url.URL)
|
||||
}
|
||||
HealthMonCheck interface {
|
||||
HealthMonitor
|
||||
HealthChecker
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user