mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-17 05:59:42 +02:00
refactor(api): restructured API for type safety, maintainability and docs generation
- These changes makes the API incombatible with previous versions - Added new types for error handling, success responses, and health checks. - Updated health check logic to utilize the new types for better clarity and structure. - Refactored existing handlers to improve response consistency and error handling. - Updated Makefile to include a new target for generating API types from Swagger. - Updated "new agent" API to respond an encrypted cert pair
This commit is contained in:
77
internal/types/docker.go
Normal file
77
internal/types/docker.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
LabelMap = map[string]any
|
||||
|
||||
PortMapping = map[int]container.Port
|
||||
Container struct {
|
||||
_ utils.NoCopy
|
||||
|
||||
DockerHost string `json:"docker_host"`
|
||||
Image *ContainerImage `json:"image"`
|
||||
ContainerName string `json:"container_name"`
|
||||
ContainerID string `json:"container_id"`
|
||||
|
||||
Agent *agent.AgentConfig `json:"agent"`
|
||||
|
||||
Labels map[string]string `json:"-"`
|
||||
IdlewatcherConfig *IdlewatcherConfig `json:"idlewatcher_config"`
|
||||
|
||||
Mounts []string `json:"mounts"`
|
||||
|
||||
Network string `json:"network,omitempty"`
|
||||
PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port
|
||||
PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port
|
||||
PublicHostname string `json:"public_hostname"`
|
||||
PrivateHostname string `json:"private_hostname"`
|
||||
|
||||
Aliases []string `json:"aliases"`
|
||||
IsExcluded bool `json:"is_excluded"`
|
||||
IsExplicit bool `json:"is_explicit"`
|
||||
IsHostNetworkMode bool `json:"is_host_network_mode"`
|
||||
Running bool `json:"running"`
|
||||
|
||||
Errors *ContainerError `json:"errors" swaggertype:"string"`
|
||||
} // @name Container
|
||||
ContainerImage struct {
|
||||
Author string `json:"author,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
} // @name ContainerImage
|
||||
|
||||
ContainerError struct {
|
||||
errs *gperr.Builder
|
||||
}
|
||||
)
|
||||
|
||||
func (e *ContainerError) Add(err error) {
|
||||
if e.errs == nil {
|
||||
e.errs = gperr.NewBuilder()
|
||||
}
|
||||
e.errs.Add(err)
|
||||
}
|
||||
|
||||
func (e *ContainerError) Error() string {
|
||||
if e.errs == nil {
|
||||
return "<niL>"
|
||||
}
|
||||
return e.errs.String()
|
||||
}
|
||||
|
||||
func (e *ContainerError) Unwrap() error {
|
||||
return e.errs.Error()
|
||||
}
|
||||
|
||||
func (e *ContainerError) MarshalJSON() ([]byte, error) {
|
||||
err := e.errs.Error().(interface{ Plain() []byte })
|
||||
return json.Marshal(string(err.Plain()))
|
||||
}
|
||||
166
internal/types/health.go
Normal file
166
internal/types/health.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type (
|
||||
HealthStatus uint8
|
||||
|
||||
HealthCheckResult struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
Detail string `json:"detail"`
|
||||
Latency time.Duration `json:"latency"`
|
||||
} // @name HealthCheckResult
|
||||
WithHealthInfo interface {
|
||||
Status() HealthStatus
|
||||
Uptime() time.Duration
|
||||
Latency() time.Duration
|
||||
Detail() string
|
||||
}
|
||||
HealthMonitor interface {
|
||||
task.TaskStarter
|
||||
task.TaskFinisher
|
||||
fmt.Stringer
|
||||
WithHealthInfo
|
||||
Name() string
|
||||
json.Marshaler
|
||||
}
|
||||
HealthChecker interface {
|
||||
CheckHealth() (result *HealthCheckResult, err error)
|
||||
URL() *url.URL
|
||||
Config() *HealthCheckConfig
|
||||
UpdateURL(url *url.URL)
|
||||
}
|
||||
HealthMonCheck interface {
|
||||
HealthMonitor
|
||||
HealthChecker
|
||||
}
|
||||
HealthJSON struct {
|
||||
Name string `json:"name"`
|
||||
Config *HealthCheckConfig `json:"config"`
|
||||
Started int64 `json:"started"`
|
||||
StartedStr string `json:"startedStr"`
|
||||
Status string `json:"status"`
|
||||
Uptime float64 `json:"uptime"`
|
||||
UptimeStr string `json:"uptimeStr"`
|
||||
Latency float64 `json:"latency"`
|
||||
LatencyStr string `json:"latencyStr"`
|
||||
LastSeen int64 `json:"lastSeen"`
|
||||
LastSeenStr string `json:"lastSeenStr"`
|
||||
Detail string `json:"detail"`
|
||||
URL string `json:"url"`
|
||||
Extra *HealthExtra `json:"extra" extensions:"x-nullable"`
|
||||
} // @name HealthJSON
|
||||
|
||||
HealthJSONRepr struct {
|
||||
Name string
|
||||
Config *HealthCheckConfig
|
||||
Status HealthStatus
|
||||
Started time.Time
|
||||
Uptime time.Duration
|
||||
Latency time.Duration
|
||||
LastSeen time.Time
|
||||
Detail string
|
||||
URL *url.URL
|
||||
Extra *HealthExtra
|
||||
}
|
||||
|
||||
HealthExtra struct {
|
||||
Config *LoadBalancerConfig `json:"config"`
|
||||
Pool map[string]any `json:"pool"`
|
||||
} // @name HealthExtra
|
||||
)
|
||||
|
||||
const (
|
||||
StatusUnknown HealthStatus = 0
|
||||
StatusHealthy HealthStatus = (1 << iota)
|
||||
StatusNapping
|
||||
StatusStarting
|
||||
StatusUnhealthy
|
||||
StatusError
|
||||
|
||||
NumStatuses int = iota - 1
|
||||
|
||||
HealthyMask = StatusHealthy | StatusNapping | StatusStarting
|
||||
IdlingMask = StatusNapping | StatusStarting
|
||||
)
|
||||
|
||||
func NewHealthStatusFromString(s string) HealthStatus {
|
||||
switch s {
|
||||
case "healthy":
|
||||
return StatusHealthy
|
||||
case "unhealthy":
|
||||
return StatusUnhealthy
|
||||
case "napping":
|
||||
return StatusNapping
|
||||
case "starting":
|
||||
return StatusStarting
|
||||
case "error":
|
||||
return StatusError
|
||||
default:
|
||||
return StatusUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (s HealthStatus) String() string {
|
||||
switch s {
|
||||
case StatusHealthy:
|
||||
return "healthy"
|
||||
case StatusUnhealthy:
|
||||
return "unhealthy"
|
||||
case StatusNapping:
|
||||
return "napping"
|
||||
case StatusStarting:
|
||||
return "starting"
|
||||
case StatusError:
|
||||
return "error"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func (s HealthStatus) Good() bool {
|
||||
return s&HealthyMask != 0
|
||||
}
|
||||
|
||||
func (s HealthStatus) Bad() bool {
|
||||
return s&HealthyMask == 0
|
||||
}
|
||||
|
||||
func (s HealthStatus) Idling() bool {
|
||||
return s&IdlingMask != 0
|
||||
}
|
||||
|
||||
func (jsonRepr *HealthJSONRepr) MarshalJSON() ([]byte, error) {
|
||||
var url string
|
||||
if jsonRepr.URL != nil {
|
||||
url = jsonRepr.URL.String()
|
||||
}
|
||||
if url == "http://:0" {
|
||||
url = ""
|
||||
}
|
||||
return json.Marshal(HealthJSON{
|
||||
Name: jsonRepr.Name,
|
||||
Config: jsonRepr.Config,
|
||||
Started: jsonRepr.Started.Unix(),
|
||||
StartedStr: strutils.FormatTime(jsonRepr.Started),
|
||||
Status: jsonRepr.Status.String(),
|
||||
Uptime: jsonRepr.Uptime.Seconds(),
|
||||
UptimeStr: strutils.FormatDuration(jsonRepr.Uptime),
|
||||
Latency: jsonRepr.Latency.Seconds(),
|
||||
LatencyStr: strconv.Itoa(int(jsonRepr.Latency.Milliseconds())) + " ms",
|
||||
LastSeen: jsonRepr.LastSeen.Unix(),
|
||||
LastSeenStr: strutils.FormatLastSeen(jsonRepr.LastSeen),
|
||||
Detail: jsonRepr.Detail,
|
||||
URL: url,
|
||||
Extra: jsonRepr.Extra,
|
||||
})
|
||||
}
|
||||
27
internal/types/healthcheck_config.go
Normal file
27
internal/types/healthcheck_config.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/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"`
|
||||
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
|
||||
|
||||
BaseContext func() context.Context `json:"-"`
|
||||
} // @name HealthCheckConfig
|
||||
|
||||
func DefaultHealthConfig() *HealthCheckConfig {
|
||||
return &HealthCheckConfig{
|
||||
Interval: common.HealthCheckIntervalDefault,
|
||||
Timeout: common.HealthCheckTimeoutDefault,
|
||||
Retries: int64(common.HealthCheckDownNotifyDelayDefault / common.HealthCheckIntervalDefault),
|
||||
}
|
||||
}
|
||||
138
internal/types/idlewatcher.go
Normal file
138
internal/types/idlewatcher.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
)
|
||||
|
||||
type (
|
||||
IdlewatcherProviderConfig struct {
|
||||
Proxmox *ProxmoxConfig `json:"proxmox,omitempty"`
|
||||
Docker *DockerConfig `json:"docker,omitempty"`
|
||||
} // @name IdlewatcherProviderConfig
|
||||
IdlewatcherConfigBase struct {
|
||||
// 0: no idle watcher.
|
||||
// Positive: idle watcher with idle timeout.
|
||||
// Negative: idle watcher as a dependency. IdleTimeout time.Duration `json:"idle_timeout" json_ext:"duration"`
|
||||
IdleTimeout time.Duration `json:"idle_timeout"`
|
||||
WakeTimeout time.Duration `json:"wake_timeout"`
|
||||
StopTimeout time.Duration `json:"stop_timeout"`
|
||||
StopMethod ContainerStopMethod `json:"stop_method"`
|
||||
StopSignal ContainerSignal `json:"stop_signal,omitempty"`
|
||||
} // @name IdlewatcherConfigBase
|
||||
IdlewatcherConfig struct {
|
||||
IdlewatcherProviderConfig
|
||||
IdlewatcherConfigBase
|
||||
|
||||
StartEndpoint string `json:"start_endpoint,omitempty"` // Optional path that must be hit to start container
|
||||
DependsOn []string `json:"depends_on,omitempty"`
|
||||
} // @name IdlewatcherConfig
|
||||
ContainerStopMethod string // @name ContainerStopMethod
|
||||
ContainerSignal string // @name ContainerSignal
|
||||
|
||||
DockerConfig struct {
|
||||
DockerHost string `json:"docker_host" validate:"required"`
|
||||
ContainerID string `json:"container_id" validate:"required"`
|
||||
ContainerName string `json:"container_name" validate:"required"`
|
||||
} // @name DockerConfig
|
||||
ProxmoxConfig struct {
|
||||
Node string `json:"node" validate:"required"`
|
||||
VMID int `json:"vmid" validate:"required"`
|
||||
} // @name ProxmoxConfig
|
||||
)
|
||||
|
||||
const (
|
||||
ContainerWakeTimeoutDefault = 30 * time.Second
|
||||
ContainerStopTimeoutDefault = 1 * time.Minute
|
||||
|
||||
ContainerStopMethodPause ContainerStopMethod = "pause"
|
||||
ContainerStopMethodStop ContainerStopMethod = "stop"
|
||||
ContainerStopMethodKill ContainerStopMethod = "kill"
|
||||
)
|
||||
|
||||
func (c *IdlewatcherConfig) Key() string {
|
||||
if c.Docker != nil {
|
||||
return c.Docker.ContainerID
|
||||
}
|
||||
return c.Proxmox.Node + ":" + strconv.Itoa(c.Proxmox.VMID)
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) ContainerName() string {
|
||||
if c.Docker != nil {
|
||||
return c.Docker.ContainerName
|
||||
}
|
||||
return "lxc-" + strconv.Itoa(c.Proxmox.VMID)
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) Validate() gperr.Error {
|
||||
if c.IdleTimeout == 0 { // zero idle timeout means no idle watcher
|
||||
return nil
|
||||
}
|
||||
errs := gperr.NewBuilder("idlewatcher config validation error")
|
||||
errs.AddRange(
|
||||
c.validateProvider(),
|
||||
c.validateTimeouts(),
|
||||
c.validateStopMethod(),
|
||||
c.validateStopSignal(),
|
||||
c.validateStartEndpoint(),
|
||||
)
|
||||
return errs.Error()
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) validateProvider() error {
|
||||
if c.Docker == nil && c.Proxmox == nil {
|
||||
return gperr.New("missing idlewatcher provider config")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) validateTimeouts() error { //nolint:unparam
|
||||
if c.WakeTimeout == 0 {
|
||||
c.WakeTimeout = ContainerWakeTimeoutDefault
|
||||
}
|
||||
if c.StopTimeout == 0 {
|
||||
c.StopTimeout = ContainerStopTimeoutDefault
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) validateStopMethod() error {
|
||||
switch c.StopMethod {
|
||||
case "":
|
||||
c.StopMethod = ContainerStopMethodStop
|
||||
return nil
|
||||
case ContainerStopMethodPause, ContainerStopMethodStop, ContainerStopMethodKill:
|
||||
return nil
|
||||
default:
|
||||
return gperr.New("invalid stop method").Subject(string(c.StopMethod))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) validateStopSignal() error {
|
||||
switch c.StopSignal {
|
||||
case "", "SIGINT", "SIGTERM", "SIGQUIT", "SIGHUP", "INT", "TERM", "QUIT", "HUP":
|
||||
return nil
|
||||
default:
|
||||
return gperr.New("invalid stop signal").Subject(string(c.StopSignal))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IdlewatcherConfig) validateStartEndpoint() error {
|
||||
if c.StartEndpoint == "" {
|
||||
return nil
|
||||
}
|
||||
// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
|
||||
// emulate browser and strip the '#' suffix prior to validation. see issue-#237
|
||||
if i := strings.Index(c.StartEndpoint, "#"); i > -1 {
|
||||
c.StartEndpoint = c.StartEndpoint[:i]
|
||||
}
|
||||
if len(c.StartEndpoint) == 0 {
|
||||
return gperr.New("start endpoint must not be empty if defined")
|
||||
}
|
||||
_, err := url.ParseRequestURI(c.StartEndpoint)
|
||||
return err
|
||||
}
|
||||
49
internal/types/idlewatcher_test.go
Normal file
49
internal/types/idlewatcher_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
expect "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
func TestValidateStartEndpoint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
input: "/start",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
input: "../foo",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "single fragment",
|
||||
input: "#",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
input: "",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cfg := new(IdlewatcherConfig)
|
||||
cfg.StartEndpoint = tc.input
|
||||
err := cfg.validateStartEndpoint()
|
||||
if err == nil {
|
||||
expect.Equal(t, cfg.StartEndpoint, tc.input)
|
||||
}
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("validateStartEndpoint() error = %v, wantErr %t", err, tc.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
54
internal/types/loadbalancer.go
Normal file
54
internal/types/loadbalancer.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type (
|
||||
LoadBalancerConfig struct {
|
||||
Link string `json:"link"`
|
||||
Mode LoadBalancerMode `json:"mode"`
|
||||
Weight int `json:"weight"`
|
||||
Options map[string]any `json:"options,omitempty"`
|
||||
} // @name LoadBalancerConfig
|
||||
LoadBalancerMode string // @name LoadBalancerMode
|
||||
LoadBalancerServer interface {
|
||||
http.Handler
|
||||
HealthMonitor
|
||||
Name() string
|
||||
Key() string
|
||||
URL() *nettypes.URL
|
||||
Weight() int
|
||||
SetWeight(weight int)
|
||||
TryWake() error
|
||||
}
|
||||
LoadBalancerServers []LoadBalancerServer
|
||||
)
|
||||
|
||||
const (
|
||||
LoadbalanceModeUnset LoadBalancerMode = ""
|
||||
LoadbalanceModeRoundRobin LoadBalancerMode = "roundrobin"
|
||||
LoadbalanceModeLeastConn LoadBalancerMode = "leastconn"
|
||||
LoadbalanceModeIPHash LoadBalancerMode = "iphash"
|
||||
)
|
||||
|
||||
func (mode *LoadBalancerMode) ValidateUpdate() bool {
|
||||
switch strutils.ToLowerNoSnake(string(*mode)) {
|
||||
case "":
|
||||
return true
|
||||
case string(LoadbalanceModeRoundRobin):
|
||||
*mode = LoadbalanceModeRoundRobin
|
||||
return true
|
||||
case string(LoadbalanceModeLeastConn):
|
||||
*mode = LoadbalanceModeLeastConn
|
||||
return true
|
||||
case string(LoadbalanceModeIPHash):
|
||||
*mode = LoadbalanceModeIPHash
|
||||
return true
|
||||
}
|
||||
*mode = LoadbalanceModeRoundRobin
|
||||
return false
|
||||
}
|
||||
63
internal/types/routes.go
Normal file
63
internal/types/routes.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/pool"
|
||||
)
|
||||
|
||||
type (
|
||||
Route interface {
|
||||
task.TaskStarter
|
||||
task.TaskFinisher
|
||||
pool.Object
|
||||
ProviderName() string
|
||||
GetProvider() RouteProvider
|
||||
TargetURL() *nettypes.URL
|
||||
HealthMonitor() HealthMonitor
|
||||
SetHealthMonitor(m HealthMonitor)
|
||||
References() []string
|
||||
|
||||
Started() <-chan struct{}
|
||||
|
||||
IdlewatcherConfig() *IdlewatcherConfig
|
||||
HealthCheckConfig() *HealthCheckConfig
|
||||
LoadBalanceConfig() *LoadBalancerConfig
|
||||
HomepageConfig() *homepage.ItemConfig
|
||||
HomepageItem() *homepage.Item
|
||||
ContainerInfo() *Container
|
||||
|
||||
GetAgent() *agent.AgentConfig
|
||||
|
||||
IsDocker() bool
|
||||
IsAgent() bool
|
||||
UseLoadBalance() bool
|
||||
UseIdleWatcher() bool
|
||||
UseHealthCheck() bool
|
||||
UseAccessLog() bool
|
||||
}
|
||||
HTTPRoute interface {
|
||||
Route
|
||||
http.Handler
|
||||
}
|
||||
ReverseProxyRoute interface {
|
||||
HTTPRoute
|
||||
ReverseProxy() *reverseproxy.ReverseProxy
|
||||
}
|
||||
StreamRoute interface {
|
||||
Route
|
||||
nettypes.Stream
|
||||
Stream() nettypes.Stream
|
||||
}
|
||||
RouteProvider interface {
|
||||
GetRoute(alias string) (r Route, ok bool)
|
||||
IterRoutes(yield func(alias string, r Route) bool)
|
||||
FindService(project, service string) (r Route, ok bool)
|
||||
ShortName() string
|
||||
}
|
||||
)
|
||||
50
internal/types/stats.go
Normal file
50
internal/types/stats.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package types
|
||||
|
||||
import provider "github.com/yusing/go-proxy/internal/route/provider/types"
|
||||
|
||||
type (
|
||||
RouteStats struct {
|
||||
Total uint16 `json:"total"`
|
||||
NumHealthy uint16 `json:"healthy"`
|
||||
NumUnhealthy uint16 `json:"unhealthy"`
|
||||
NumNapping uint16 `json:"napping"`
|
||||
NumError uint16 `json:"error"`
|
||||
NumUnknown uint16 `json:"unknown"`
|
||||
} // @name RouteStats
|
||||
ProviderStats struct {
|
||||
Total uint16 `json:"total"`
|
||||
RPs RouteStats `json:"reverse_proxies"`
|
||||
Streams RouteStats `json:"streams"`
|
||||
Type provider.Type `json:"type"`
|
||||
} // @name ProviderStats
|
||||
)
|
||||
|
||||
func (stats *RouteStats) Add(r Route) {
|
||||
stats.Total++
|
||||
mon := r.HealthMonitor()
|
||||
if mon == nil {
|
||||
stats.NumUnknown++
|
||||
return
|
||||
}
|
||||
switch mon.Status() {
|
||||
case StatusHealthy:
|
||||
stats.NumHealthy++
|
||||
case StatusUnhealthy:
|
||||
stats.NumUnhealthy++
|
||||
case StatusNapping:
|
||||
stats.NumNapping++
|
||||
case StatusError:
|
||||
stats.NumError++
|
||||
default:
|
||||
stats.NumUnknown++
|
||||
}
|
||||
}
|
||||
|
||||
func (stats *RouteStats) AddOther(other RouteStats) {
|
||||
stats.Total += other.Total
|
||||
stats.NumHealthy += other.NumHealthy
|
||||
stats.NumUnhealthy += other.NumUnhealthy
|
||||
stats.NumNapping += other.NumNapping
|
||||
stats.NumError += other.NumError
|
||||
stats.NumUnknown += other.NumUnknown
|
||||
}
|
||||
Reference in New Issue
Block a user