Compare commits

..

64 commits

Author SHA1 Message Date
yusing
f179de9231 fix(setup): update DNS provider docs link in setup script 2025-06-15 10:00:53 +08:00
yusing
1d546624de fix(serialization): call of reflect.Value.IsNil on string Value 2025-06-14 22:12:24 +08:00
yusing
ecc9d306d1 refactor(agent): move agent pool to agent package, rename route.Agent() to route.GetAgent() (cont. 7d17a01) 2025-06-14 22:04:10 +08:00
yusing
5ce1c7865e feat(agent): allow specifying agent for routes in route files 2025-06-14 20:05:11 +08:00
yusing
7d17a01de1 refactor(agent): move agent pool to agent package, rename route.Agent() to route.GetAgent() 2025-06-14 20:04:39 +08:00
yusing
cabb840a91 tweak(docker): add hint when specified network not found 2025-06-14 19:32:36 +08:00
yusing
4825f768f3 feat(docker): allow specifying docker network, handle error when no network available 2025-06-14 17:08:07 +08:00
yusing
5fdb023188 feat(docker): add network field to container info 2025-06-14 10:05:45 +08:00
yusing
4abf61a421 refactor(notif): enhance retry mechanism with exponential backoff and jitter; replace retry channel with a set for managing retry messages 2025-06-14 09:31:09 +08:00
yusing
96b7c3fcec chore: upgrade dependenocies
Some checks failed
Docker Image CI (socket-proxy) / build (push) Has been cancelled
2025-06-13 23:06:34 +08:00
yusing
f8c57d930f fix(docker): wildcard labels not applying properly for YAML style values and alias without labels 2025-06-13 23:02:25 +08:00
yusing
880d66c75e docs: update links in config.example.yml to point to the new documentation site 2025-06-12 21:34:50 +08:00
yusing
4649c8d479 chore: update .gitignore to include .cursor directory 2025-06-12 21:08:00 +08:00
DarinDev1000
20021b3cae add GODOXY_API_JWT_SECURE=true to .env 2025-06-12 21:08:00 +08:00
yusing
cfa9201f82 fix(shutdown): change gracefulShutdown to call root.Finish directly instead of in a goroutine 2025-06-09 22:20:49 +08:00
yusing
b5328fe5e7 feat(idlesleep): support idlesleep for stream routes, rewritten and fixed stream implementation 2025-06-09 22:20:26 +08:00
yusing
25fbcc4ab9 fix(label): expand wildcard labels before unmarshaling and add corresponding test 2025-06-09 20:46:39 +08:00
yusing
421aaecba4 refactor: rename net/types to nettypes 2025-06-08 17:59:48 +08:00
yusing
01773976d1 fix(compose): rename container from godoxy to godoxy-proxy to prevent route conflict 2025-06-08 17:55:17 +08:00
yusing
2263d6063e fix(favicon): add GetFavIconFromAlias function and update favicon handling in idlewatcher 2025-06-08 15:13:38 +08:00
yusing
cfe0f6bb70 fix(route): remove x-properties routes during loading
Some checks failed
Docker Image CI (socket-proxy) / build (push) Has been cancelled
2025-06-07 22:28:44 +08:00
yusing
a90d2b90d1 fix(systeminfo): safer time interval calculations and fix divided by zero crash 2025-06-07 19:42:53 +08:00
yusing
af9629424e fix(route): incorrect url for loadbalanced route 2025-06-07 19:13:31 +08:00
yusing
ee6cf29bc1 chore: upgrade dependenocies 2025-06-07 19:05:04 +08:00
yusing
c4a780e061 chore: updated to go1.24.4 2025-06-07 18:54:23 +08:00
yusing
09c244ef3c fix(route): add mutex lock for load balancer updates to prevent race conditions 2025-06-05 18:53:11 +08:00
yusing
bd0fe36c53 fix(idlewatcher): should not print idle_timeout fields on dependencies 2025-06-05 18:49:11 +08:00
yusing
d240da4393 fix(route): incorrect health status for idlewatcher dependencies 2025-06-05 18:40:40 +08:00
yusing
9470a14fe8 refactor(route): unify common fields into routes.go 2025-06-05 18:25:15 +08:00
yusing
d3568d9c35 fix: conflict error on load-balanced and excluded routes 2025-06-05 01:16:53 +08:00
yusing
44ef351840 fix(panic): Route.ProviderName before provider is set 2025-06-05 00:13:29 +08:00
yusing
a39d527fc1 feat(idlesleep): support container dependencies, including custom and docker depends_on, code refactor 2025-06-04 23:26:38 +08:00
yusing
22ab043e06 refactor(route): improve route handling 2025-06-04 23:17:41 +08:00
yusing
b670cdbd49 refactor(provider): improve route handling 2025-06-04 23:15:56 +08:00
yusing
45e34d691a tweak(healthcheck): allow custom base context 2025-06-04 23:14:46 +08:00
yusing
e82480a639 refactor: rename route/provider/types to provider 2025-06-04 23:13:42 +08:00
yusing
e39407886d fix: improved docker image parsing 2025-06-04 23:00:53 +08:00
yusing
3135e377a9 tweak(route): start routes in parallel 2025-06-03 23:32:59 +08:00
yusing
bdb3343a7c fix(healthcheck): handle cases for zero port 2025-06-03 22:56:00 +08:00
yusing
b411c6d504 feat(route): add api info for whether route is excluded 2025-06-03 22:48:35 +08:00
yusing
966a59b5c9 tweak: improve port and scheme detection 2025-06-03 22:41:31 +08:00
yusing
58db228e25 refactor(query): replace anonymous functions with sequence and for loop 2025-06-03 20:51:00 +08:00
yusing
e737737415 fix(idlewatcher): wake time outs before actual timeout 2025-06-02 23:26:47 +08:00
yusing
9087c4f195 feat(healthcheck): allow health checking for excluded routes 2025-06-02 23:19:30 +08:00
yusing
4705989f4b fix(websocket): 403 forbidden agent metrics 2025-06-01 00:31:56 +08:00
yusing
cb506120dd fix(serialization): remove debug stack from error message in mapUnmarshalValidate 2025-05-31 22:51:30 +08:00
yusing
88aaf956e5 fix(ci): fix wrong indentation for commented out gha cache in Docker workflow 2025-05-31 22:42:44 +08:00
yusing
ecfd018b0b fix(websocket): http: superfluous response.WriteHeader on websocket initiation failure 2025-05-31 22:29:02 +08:00
yusing
54bf84dcba fix(websocket): fix local address checks 2025-05-31 13:55:29 +08:00
yusing
57200bc1e9 refactor(io): enhance HTTP flusher handling 2025-05-31 13:54:50 +08:00
yusing
6f9bb410f5 fix(agent): use godoxy-to-agent latency for health check 2025-05-31 07:39:01 +08:00
yusing
e62e667b49 ci: remove gha cache in Docker workflow 2025-05-31 07:15:39 +08:00
yusing
abe81541db fix(login): login not working with user password 2025-05-31 07:11:19 +08:00
yusing
9e5d33714c chore: update dependencies
Some checks failed
Docker Image CI (socket-proxy) / build (push) Has been cancelled
2025-05-30 20:50:31 +08:00
yusing
93a81fd558 fix(task): fix task tests 2025-05-29 20:42:36 +08:00
yusing
72923b8cfa refactor: remove Tracer from middleware implementations and related debugging functionality 2025-05-29 20:27:25 +08:00
yusing
24ba4c2a46 fix(oidc): update login handler to set redirect header for frontend requests 2025-05-29 20:08:52 +08:00
yusing
ed07bf42ce fix(routes): accidentally finished all routes when one route changed 2025-05-29 19:22:42 +08:00
yusing
371e756307 refactor: small refactor on maxmind code 2025-05-28 22:14:10 +08:00
yusing
32d8292b17 fix(oidc): apply rate limit to fix oocasional oauth state error due to race condition 2025-05-28 22:12:41 +08:00
yusing
717fd0e58c feat(net): add option to disable compression
Some checks failed
Docker Image CI (nightly) / build-nightly (push) Has been cancelled
Docker Image CI (nightly) / build-nightly-agent (push) Has been cancelled
2025-05-28 22:09:25 +08:00
yusing
2628d9e8a8 fix(task): refactor task module and fix reload stuck/error, fix some logic 2025-05-28 22:07:13 +08:00
yusing
c90795e614 fix(notification): unknown format: &{} 2025-05-26 20:03:17 +08:00
yusing
4a6bed7728 refactor: headers handling 2025-05-26 19:57:39 +08:00
125 changed files with 3119 additions and 2092 deletions

View file

@ -8,6 +8,8 @@ TZ=ETC/UTC
GODOXY_UID=1000 GODOXY_UID=1000
GODOXY_GID=1000 GODOXY_GID=1000
# Set GODOXY_API_JWT_SECURE=false to allow http
GODOXY_API_JWT_SECURE=true
# API JWT Configuration (common) # API JWT Configuration (common)
# generate secret with `openssl rand -base64 32` # generate secret with `openssl rand -base64 32`
GODOXY_API_JWT_SECRET= GODOXY_API_JWT_SECRET=

View file

@ -84,10 +84,10 @@ jobs:
outputs: type=image,name=${{ env.REGISTRY }}/${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true outputs: type=image,name=${{ env.REGISTRY }}/${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true
cache-from: | cache-from: |
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }} type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }}
type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }} # type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }}
cache-to: | cache-to: |
type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image_name }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max
type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }},mode=max # type=gha,scope=${{ github.workflow }}-${{ env.PLATFORM_PAIR }},mode=max
build-args: | build-args: |
VERSION=${{ github.ref_name }} VERSION=${{ github.ref_name }}
MAKE_ARGS=${{ env.MAKE_ARGS }} MAKE_ARGS=${{ env.MAKE_ARGS }}

1
.gitignore vendored
View file

@ -30,6 +30,7 @@ todo.md
mtrace.json mtrace.json
.env .env
.cursorrules .cursorrules
.cursor/
.windsurfrules .windsurfrules
test.Dockerfile test.Dockerfile

View file

@ -1,5 +1,5 @@
# Stage 1: deps # Stage 1: deps
FROM golang:1.24.3-alpine AS deps FROM golang:1.24.4-alpine AS deps
HEALTHCHECK NONE HEALTHCHECK NONE
# package version does not matter # package version does not matter

View file

@ -31,7 +31,7 @@ endif
ifeq ($(debug), 1) ifeq ($(debug), 1)
CGO_ENABLED = 0 CGO_ENABLED = 0
GODOXY_DEBUG = 1 GODOXY_DEBUG = 1
BUILD_FLAGS += -gcflags=all='-N -l' -tags debug BUILD_FLAGS += -gcflags=all='-N -l' -tags debug -asan
else ifeq ($(pprof), 1) else ifeq ($(pprof), 1)
CGO_ENABLED = 1 CGO_ENABLED = 1
GORACE = log_path=logs/pprof strip_path_prefix=$(shell pwd)/ halt_on_error=1 GORACE = log_path=logs/pprof strip_path_prefix=$(shell pwd)/ halt_on_error=1

View file

@ -1,6 +1,6 @@
module github.com/yusing/go-proxy/agent module github.com/yusing/go-proxy/agent
go 1.24.3 go 1.24.4
replace github.com/yusing/go-proxy => .. replace github.com/yusing/go-proxy => ..
@ -10,13 +10,13 @@ replace github.com/yusing/go-proxy/internal/utils => ../internal/utils
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30 replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30
replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327 replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250607110153-34d627ba1b5d
require ( require (
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/rs/zerolog v1.34.0 github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/yusing/go-proxy v0.0.0-00010101000000-000000000000 github.com/yusing/go-proxy v0.14.2
github.com/yusing/go-proxy/internal/utils v0.0.0 github.com/yusing/go-proxy/internal/utils v0.0.0
github.com/yusing/go-proxy/socketproxy v0.0.0-00010101000000-000000000000 github.com/yusing/go-proxy/socketproxy v0.0.0-00010101000000-000000000000
) )
@ -33,8 +33,8 @@ require (
github.com/diskfs/go-diskfs v1.6.0 // indirect github.com/diskfs/go-diskfs v1.6.0 // indirect
github.com/distribution/reference v0.6.0 // indirect github.com/distribution/reference v0.6.0 // indirect
github.com/djherbis/times v1.6.0 // indirect github.com/djherbis/times v1.6.0 // indirect
github.com/docker/cli v28.1.1+incompatible // indirect github.com/docker/cli v28.2.2+incompatible // indirect
github.com/docker/docker v28.1.1+incompatible // indirect github.com/docker/docker v28.2.2+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.4 // indirect github.com/ebitengine/purego v0.8.4 // indirect
@ -46,9 +46,9 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/goccy/go-yaml v1.17.1 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/mux v1.8.1 // indirect
github.com/gotify/server/v2 v2.6.3 // indirect github.com/gotify/server/v2 v2.6.3 // indirect
github.com/jinzhu/copier v0.4.0 // indirect github.com/jinzhu/copier v0.4.0 // indirect
@ -71,10 +71,10 @@ require (
github.com/puzpuzpuz/xsync/v4 v4.1.0 // indirect github.com/puzpuzpuz/xsync/v4 v4.1.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.52.0 // indirect github.com/quic-go/quic-go v0.52.0 // indirect
github.com/samber/lo v1.50.0 // indirect github.com/samber/lo v1.51.0 // indirect
github.com/samber/slog-common v0.18.1 // indirect github.com/samber/slog-common v0.18.1 // indirect
github.com/samber/slog-zerolog/v2 v2.7.3 // indirect github.com/samber/slog-zerolog/v2 v2.7.3 // indirect
github.com/shirou/gopsutil/v4 v4.25.4 // indirect github.com/shirou/gopsutil/v4 v4.25.5 // indirect
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
github.com/spf13/afero v1.14.0 // indirect github.com/spf13/afero v1.14.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect
@ -84,13 +84,13 @@ require (
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/mock v0.5.2 // indirect go.uber.org/mock v0.5.2 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/crypto v0.39.0 // indirect
golang.org/x/mod v0.24.0 // indirect golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.40.0 // indirect golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.11.0 // indirect golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.34.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View file

@ -29,8 +29,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@ -45,8 +45,8 @@ github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV
github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI= github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI=
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY= github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw= github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@ -64,20 +64,20 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30 h1:+5pYG8clUrZbUDP+x149jkRfYAGaNpAXOwut0jluoYA= github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30 h1:+5pYG8clUrZbUDP+x149jkRfYAGaNpAXOwut0jluoYA=
github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30/go.mod h1:7VkicOZ3VrlxOe/EP/8uwsWLGKI2wt2MV7CgxTDIYgA= github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30/go.mod h1:7VkicOZ3VrlxOe/EP/8uwsWLGKI2wt2MV7CgxTDIYgA=
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327 h1:MyHi1+oJ5hVIYpRoQg9YaWPcz0XdlUxuyJix2klbIVo= github.com/godoxy-app/gopsutil/v4 v4.0.0-20250607110153-34d627ba1b5d h1:kih+Q38BQKBsbQmv7mZb7OP3YuSPN78ENTZUxCu4T6M=
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327/go.mod h1:2nclxpbWQUvbTR33HI8Z/RXUG4SaF67X/pMaI/fUMa8= github.com/godoxy-app/gopsutil/v4 v4.0.0-20250607110153-34d627ba1b5d/go.mod h1:2nclxpbWQUvbTR33HI8Z/RXUG4SaF67X/pMaI/fUMa8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
@ -98,6 +98,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@ -129,8 +131,6 @@ github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
@ -163,8 +163,8 @@ github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ= github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk= github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY= github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
@ -220,8 +220,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -229,8 +229,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -243,8 +243,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -254,8 +254,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -297,10 +297,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -309,8 +309,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -318,15 +318,15 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,57 @@
package agent
import (
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/utils/functional"
)
var agentPool = functional.NewMapOf[string, *AgentConfig]()
func init() {
if common.IsTest {
agentPool.Store("test-agent", &AgentConfig{
Addr: "test-agent",
})
}
}
func GetAgent(agentAddrOrDockerHost string) (*AgentConfig, bool) {
if !IsDockerHostAgent(agentAddrOrDockerHost) {
return getAgentByAddr(agentAddrOrDockerHost)
}
return getAgentByAddr(GetAgentAddrFromDockerHost(agentAddrOrDockerHost))
}
func GetAgentByName(name string) (*AgentConfig, bool) {
for _, agent := range agentPool.Range {
if agent.Name() == name {
return agent, true
}
}
return nil, false
}
func AddAgent(agent *AgentConfig) {
agentPool.Store(agent.Addr, agent)
}
func RemoveAgent(agent *AgentConfig) {
agentPool.Delete(agent.Addr)
}
func RemoveAllAgents() {
agentPool.Clear()
}
func ListAgents() []*AgentConfig {
agents := make([]*AgentConfig, 0, agentPool.Size())
for _, agent := range agentPool.Range {
agents = append(agents, agent)
}
return agents
}
func getAgentByAddr(addr string) (agent *AgentConfig, ok bool) {
agent, ok = agentPool.Load(addr)
return
}

View file

@ -50,7 +50,7 @@ services:
# - 172.16.0.0/12 # - 172.16.0.0/12
app: app:
image: ghcr.io/yusing/godoxy:${TAG:-latest} image: ghcr.io/yusing/godoxy:${TAG:-latest}
container_name: godoxy container_name: godoxy-proxy
restart: always restart: always
network_mode: host # do not change this network_mode: host # do not change this
env_file: .env env_file: .env

View file

@ -15,7 +15,7 @@
# options: # options:
# auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token # auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token
# 3. other providers, see https://github.com/yusing/godoxy/wiki/Supported-DNS%E2%80%9001-Providers#supported-dns-01-providers # 3. other providers, see https://docs.godoxy.dev/DNS-01-Providers
# acl: # acl:
# default: allow # or deny (default: allow) # default: allow # or deny (default: allow)
@ -115,7 +115,7 @@ providers:
# secret: aaaa-bbbb-cccc-dddd # secret: aaaa-bbbb-cccc-dddd
# no_tls_verify: true # no_tls_verify: true
# Check https://github.com/yusing/godoxy/wiki/Certificates-and-domain-matching#domain-matching # Check https://docs.godoxy.dev/Certificates-and-domain-matching
# for explaination of `match_domains` # for explaination of `match_domains`
# #
# match_domains: # match_domains:

98
go.mod
View file

@ -1,6 +1,6 @@
module github.com/yusing/go-proxy module github.com/yusing/go-proxy
go 1.24.3 go 1.24.4
replace github.com/yusing/go-proxy/agent => ./agent replace github.com/yusing/go-proxy/agent => ./agent
@ -12,12 +12,12 @@ replace github.com/coreos/go-oidc/v3 => github.com/godoxy-app/go-oidc/v3 v3.0.0-
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30 replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30
replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327 replace github.com/shirou/gopsutil/v4 => github.com/godoxy-app/gopsutil/v4 v4.0.0-20250607110153-34d627ba1b5d
require ( require (
github.com/PuerkitoBio/goquery v1.10.3 // parsing HTML for extract fav icon github.com/PuerkitoBio/goquery v1.10.3 // parsing HTML for extract fav icon
github.com/coreos/go-oidc/v3 v3.14.1 // oidc authentication github.com/coreos/go-oidc/v3 v3.14.1 // oidc authentication
github.com/docker/docker v28.1.1+incompatible // docker daemon github.com/docker/docker v28.2.2+incompatible // docker daemon
github.com/fsnotify/fsnotify v1.9.0 // file watcher github.com/fsnotify/fsnotify v1.9.0 // file watcher
github.com/go-acme/lego/v4 v4.23.1 // acme client github.com/go-acme/lego/v4 v4.23.1 // acme client
github.com/go-playground/validator/v10 v10.26.0 // validator github.com/go-playground/validator/v10 v10.26.0 // validator
@ -27,17 +27,18 @@ require (
github.com/lithammer/fuzzysearch v1.1.8 // fuzzy search for searching icons and filtering metrics github.com/lithammer/fuzzysearch v1.1.8 // fuzzy search for searching icons and filtering metrics
github.com/puzpuzpuz/xsync/v4 v4.1.0 // lock free map for concurrent operations github.com/puzpuzpuz/xsync/v4 v4.1.0 // lock free map for concurrent operations
github.com/rs/zerolog v1.34.0 // logging github.com/rs/zerolog v1.34.0 // logging
github.com/shirou/gopsutil/v4 v4.25.4 // system info metrics github.com/shirou/gopsutil/v4 v4.25.5 // system info metrics
github.com/vincent-petithory/dataurl v1.0.0 // data url for fav icon github.com/vincent-petithory/dataurl v1.0.0 // data url for fav icon
golang.org/x/crypto v0.38.0 // encrypting password with bcrypt golang.org/x/crypto v0.39.0 // encrypting password with bcrypt
golang.org/x/net v0.40.0 // HTTP header utilities golang.org/x/net v0.41.0 // HTTP header utilities
golang.org/x/oauth2 v0.30.0 // oauth2 authentication golang.org/x/oauth2 v0.30.0 // oauth2 authentication
golang.org/x/time v0.11.0 // time utilities golang.org/x/sync v0.15.0
golang.org/x/time v0.12.0 // time utilities
) )
require ( require (
github.com/docker/cli v28.1.1+incompatible github.com/docker/cli v28.2.2+incompatible
github.com/goccy/go-yaml v1.17.1 // yaml parsing for different config files github.com/goccy/go-yaml v1.18.0 // yaml parsing for different config files
github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang-jwt/jwt/v5 v5.2.2
github.com/luthermonson/go-proxmox v0.2.2 github.com/luthermonson/go-proxmox v0.2.2
github.com/oschwald/maxminddb-golang v1.13.1 github.com/oschwald/maxminddb-golang v1.13.1
@ -45,18 +46,18 @@ require (
github.com/samber/slog-zerolog/v2 v2.7.3 github.com/samber/slog-zerolog/v2 v2.7.3
github.com/spf13/afero v1.14.0 github.com/spf13/afero v1.14.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/yusing/go-proxy/agent v0.0.0-00010101000000-000000000000 github.com/yusing/go-proxy/agent v0.0.0-20250612133450-880d66c75e3f
github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-00010101000000-000000000000 github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-20250612133450-880d66c75e3f
github.com/yusing/go-proxy/internal/utils v0.0.0 github.com/yusing/go-proxy/internal/utils v0.0.0
) )
require ( require (
cloud.google.com/go/auth v0.16.1 // indirect cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
@ -67,27 +68,27 @@ require (
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.16 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.69 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 // indirect github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 // indirect
github.com/aws/smithy-go v1.22.3 // indirect github.com/aws/smithy-go v1.22.3 // indirect
github.com/baidubce/bce-sdk-go v0.9.228 // indirect github.com/baidubce/bce-sdk-go v0.9.230 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect github.com/benbjohnson/clock v1.3.5 // indirect
github.com/boombuler/barcode v1.0.2 // indirect github.com/boombuler/barcode v1.0.2 // indirect
github.com/buger/goterm v1.0.4 // indirect github.com/buger/goterm v1.0.4 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/civo/civogo v0.5.3 // indirect github.com/civo/civogo v0.6.1 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/diskfs/go-diskfs v1.6.0 // indirect github.com/diskfs/go-diskfs v1.6.0 // indirect
@ -97,14 +98,14 @@ require (
github.com/docker/go-connections v0.5.0 github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.4 // indirect github.com/ebitengine/purego v0.8.4 // indirect
github.com/exoscale/egoscale/v3 v3.1.18 // indirect github.com/exoscale/egoscale/v3 v3.1.20 // indirect
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/go-errors/errors v1.5.1 // indirect github.com/go-errors/errors v1.5.1 // indirect
github.com/go-jose/go-jose/v4 v4.1.0 // indirect github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
@ -116,7 +117,7 @@ require (
github.com/gofrs/flock v0.12.1 // indirect github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
github.com/google/s2a-go v0.1.9 // indirect github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
@ -126,7 +127,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect github.com/jinzhu/copier v0.4.0 // indirect
@ -138,7 +139,7 @@ require (
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect github.com/labbsr0x/goh v1.0.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/linode/linodego v1.51.0 // indirect github.com/linode/linodego v1.52.1 // indirect
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
github.com/liquidweb/liquidweb-go v1.6.4 // indirect github.com/liquidweb/liquidweb-go v1.6.4 // indirect
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
@ -168,7 +169,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v65 v65.91.1 // indirect github.com/oracle/oci-go-sdk/v65 v65.93.1 // indirect
github.com/ovh/go-ovh v1.7.0 // indirect github.com/ovh/go-ovh v1.7.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
@ -183,10 +184,10 @@ require (
github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/sacloud/api-client-go v0.3.0 // indirect github.com/sacloud/api-client-go v0.3.0 // indirect
github.com/sacloud/go-http v0.1.9 // indirect github.com/sacloud/go-http v0.1.9 // indirect
github.com/sacloud/iaas-api-go v1.15.0 // indirect github.com/sacloud/iaas-api-go v1.16.0 // indirect
github.com/sacloud/packages-go v0.0.11 // indirect github.com/sacloud/packages-go v0.0.11 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/samber/lo v1.50.0 // indirect github.com/samber/lo v1.51.0 // indirect
github.com/samber/slog-common v0.18.1 // indirect github.com/samber/slog-common v0.18.1 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
github.com/selectel/domains-go v1.1.0 // indirect github.com/selectel/domains-go v1.1.0 // indirect
@ -198,42 +199,41 @@ require (
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/sony/gobreaker v1.0.0 // indirect github.com/sony/gobreaker v1.0.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.8.0 // indirect github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.20.1 // indirect github.com/spf13/viper v1.20.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1170 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect github.com/tklauser/numcpus v0.10.0 // indirect
github.com/transip/gotransip/v6 v6.26.0 // indirect github.com/transip/gotransip/v6 v6.26.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/volcengine/volc-sdk-golang v1.0.209 // indirect github.com/volcengine/volc-sdk-golang v1.0.211 // indirect
github.com/vultr/govultr/v3 v3.20.0 // indirect github.com/vultr/govultr/v3 v3.20.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.17.3 // indirect go.mongodb.org/mongo-driver v1.17.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0
go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/mock v0.5.2 // indirect go.uber.org/mock v0.5.2 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/ratelimit v0.3.1 // indirect
golang.org/x/mod v0.24.0 // indirect golang.org/x/mod v0.25.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.34.0 // indirect
google.golang.org/api v0.234.0 // indirect google.golang.org/api v0.237.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.72.1 // indirect google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
@ -243,7 +243,7 @@ require (
k8s.io/api v0.33.1 // indirect k8s.io/api v0.33.1 // indirect
k8s.io/apimachinery v0.33.1 // indirect k8s.io/apimachinery v0.33.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect

174
go.sum
View file

@ -97,8 +97,8 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo
cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo=
cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
@ -606,8 +606,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesn
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
@ -678,40 +678,40 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= github.com/aws/aws-sdk-go-v2/config v1.29.16 h1:XkruGnXX1nEZ+Nyo9v84TzsX+nj86icbFAeust6uo8A=
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= github.com/aws/aws-sdk-go-v2/config v1.29.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= github.com/aws/aws-sdk-go-v2/credentials v1.17.69/go.mod h1:gPME6I8grR1jCqBFEGthULiolzf/Sexq/Wy42ibKK9c=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 h1:/ldKrPPXTC421bTNWrUIpq3CxwHwRI/kpc+jPUTJocM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 h1:8NcRiAZn54D1W6rQZaZfZtjnLH07WciOhDcYIKpVKQM=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2/go.mod h1:Qy22QnQSdHbZwMZrarsWZBIuK51isPlkD+Z4sztxX0o= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3/go.mod h1:nramkVwrN/StFgVfTZp6Azi1J4lWnq+99fwpJRTasj4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 h1:41HrH51fydStW2Tah74zkqZlJfyx4gXeuGOdsIFuckY= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 h1:PAbznrQ8b8IwTUJgBdcbVqc+r57SO3jy0YJi9bJKPmQ=
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1/go.mod h1:cpFFGJ0A6WKZjf26TVzYI3qFhbFXXb7xeF5bOOMax6c=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 h1:EU58LP8ozQDVroOEyAfcq0cGc5R/FTZjVoYJ6tvby3w=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 h1:XB4z0hbQtpmBnb1FQYvKaCM7UsS6Y/u8jVBwIUGeCTk=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 h1:nyLjs8sYJShFYj6aiyjCBI3EcLn1udWrQTjEF+SOXB0=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/baidubce/bce-sdk-go v0.9.228 h1:XEY3/oAxXcsi7+3atib9fMI6YNE9sL5qo+WMZ+iqmNE= github.com/baidubce/bce-sdk-go v0.9.230 h1:HzELBKiD7QAgYqZ1qHZexoI2A3Lo/6zYGQFvcUbS5cA=
github.com/baidubce/bce-sdk-go v0.9.228/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/baidubce/bce-sdk-go v0.9.230/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -751,8 +751,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/civo/civogo v0.5.3 h1:FLtiosmNxHErTPonCouosPmg9C+3itmI/qxNxuPQIZs= github.com/civo/civogo v0.6.1 h1:PFOh7rBU0vmj7LTDIv3z7l9uXG4SZyyzScCl3wyTFSc=
github.com/civo/civogo v0.5.3/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.6.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
@ -803,8 +803,8 @@ github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY= github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8= github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@ -835,8 +835,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/exoscale/egoscale/v3 v3.1.18 h1:9LvH61Cd0L/JC04KartbICl/hVva5AAwtTzzOxLtSYA= github.com/exoscale/egoscale/v3 v3.1.20 h1:AZw7VWblEM4+77bjFt9pHBvOQj+spqLkDVxGxK7cbUU=
github.com/exoscale/egoscale/v3 v3.1.18/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/exoscale/egoscale/v3 v3.1.20/go.mod h1:A53enXfm8nhVMpIYw0QxiwQ2P6AdCF4F/nVYChNEzdE=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
@ -895,8 +895,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@ -930,15 +930,15 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30 h1:+5pYG8clUrZbUDP+x149jkRfYAGaNpAXOwut0jluoYA= github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30 h1:+5pYG8clUrZbUDP+x149jkRfYAGaNpAXOwut0jluoYA=
github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30/go.mod h1:7VkicOZ3VrlxOe/EP/8uwsWLGKI2wt2MV7CgxTDIYgA= github.com/godoxy-app/docker v0.0.0-20250523125835-a2474a6ebe30/go.mod h1:7VkicOZ3VrlxOe/EP/8uwsWLGKI2wt2MV7CgxTDIYgA=
github.com/godoxy-app/go-oidc/v3 v3.0.0-20250523122447-f078841dec22 h1:cGUREXizw65YGgjh1gLgEk+d4/Ts3JmZASro48TlG0k= github.com/godoxy-app/go-oidc/v3 v3.0.0-20250523122447-f078841dec22 h1:cGUREXizw65YGgjh1gLgEk+d4/Ts3JmZASro48TlG0k=
github.com/godoxy-app/go-oidc/v3 v3.0.0-20250523122447-f078841dec22/go.mod h1:x2nHY0TayhBzI25mgwp38Ru7xgGRId0FdaeqXeH8l90= github.com/godoxy-app/go-oidc/v3 v3.0.0-20250523122447-f078841dec22/go.mod h1:x2nHY0TayhBzI25mgwp38Ru7xgGRId0FdaeqXeH8l90=
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327 h1:MyHi1+oJ5hVIYpRoQg9YaWPcz0XdlUxuyJix2klbIVo= github.com/godoxy-app/gopsutil/v4 v4.0.0-20250607110153-34d627ba1b5d h1:kih+Q38BQKBsbQmv7mZb7OP3YuSPN78ENTZUxCu4T6M=
github.com/godoxy-app/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327/go.mod h1:2nclxpbWQUvbTR33HI8Z/RXUG4SaF67X/pMaI/fUMa8= github.com/godoxy-app/gopsutil/v4 v4.0.0-20250607110153-34d627ba1b5d/go.mod h1:2nclxpbWQUvbTR33HI8Z/RXUG4SaF67X/pMaI/fUMa8=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
@ -1039,8 +1039,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
@ -1154,8 +1154,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 h1:Ih+z79Ko1ClH4dlv7O1lyHRiVCjkb2NZYYk+1cSZbU8= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 h1:vqrLDi/UZpBEliQXLfdavmtETgGLj82iU8Tja0URzqI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -1239,8 +1239,8 @@ github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/linode/linodego v1.51.0 h1:8S0vNQRUZAuumGFe8h/huCzbyExxlvOUR2Uf0oDoJ/8= github.com/linode/linodego v1.52.1 h1:HJ1cz1n9n3chRP9UrtqmP91+xTi0Q5l+H/4z4tpkwgQ=
github.com/linode/linodego v1.51.0/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw= github.com/linode/linodego v1.52.1/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw=
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
@ -1402,8 +1402,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oracle/oci-go-sdk/v65 v65.91.1 h1:jRE4jUiJd+sDhJTyTCXIJdzLjTx+99gA3PgzpVTaF/I= github.com/oracle/oci-go-sdk/v65 v65.93.1 h1:lIvy/6aQOUenQI+cxXH1wDBJeXFPO9Du3CaomXeYFaY=
github.com/oracle/oci-go-sdk/v65 v65.91.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.93.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
@ -1517,15 +1517,15 @@ github.com/sacloud/api-client-go v0.3.0 h1:NLA2BpZ5welAXBPxgmXSCjgn/k0ShDB0x5kmU
github.com/sacloud/api-client-go v0.3.0/go.mod h1:v8ke3pxVQ9TCWEbMkfbLX8NMeGiPpE+uDwlgcUWfMgM= github.com/sacloud/api-client-go v0.3.0/go.mod h1:v8ke3pxVQ9TCWEbMkfbLX8NMeGiPpE+uDwlgcUWfMgM=
github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE= github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE= github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE=
github.com/sacloud/iaas-api-go v1.15.0 h1:G88U69OOVUSjOZ2fEP5QWMg7r3VOVsfNkl5Mxl+/wMk= github.com/sacloud/iaas-api-go v1.16.0 h1:JUj7f5yHSSakhy0N8qsZ0a5IYpbUgsv4x5rN8pgRbEg=
github.com/sacloud/iaas-api-go v1.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA= github.com/sacloud/iaas-api-go v1.16.0/go.mod h1:8BTuDTCTT6Ie08544+q5hT3GMyTf2DDxzt/XMuwmhgk=
github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo= github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo=
github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ= github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk= github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY= github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
@ -1577,8 +1577,8 @@ github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZ
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@ -1619,11 +1619,11 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1170/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1184/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172 h1:cfh0m0esFTjBZ6YZRCVWUDs772KOfxQR/mKY7zL8/qg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 h1:LlFJcouP8DMN4tLWfF9oYG+x4tzd40ISMMZ8MaGNH80=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1170 h1:qdjh5LP8N2bE8cPfBX6Y3S/3+5yD2JLJBx7EALVrgP0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 h1:nZ6YVgr1X332hEI8bQEyW4Hhp9FFOYEZ6Q8fSxND2pQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1170/go.mod h1:OS8GJ/x+p/6Wchc6Lp6iA9Gf9sQ/it0xatH1AXGX8PY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184/go.mod h1:nigwfh7wyX/yj24MdEEq++yvsPp1XkSczazhb0wzYts=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
@ -1647,8 +1647,8 @@ github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8A
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/volcengine/volc-sdk-golang v1.0.209 h1:aYCVO2fQ4meqLZ+XjVLcKzDn7kYfrUekqp48iioH3ug= github.com/volcengine/volc-sdk-golang v1.0.211 h1:FgwD+1phyy+un4Qk2YqooYtp6XpvNDQ4a/fpsCAtDHo=
github.com/volcengine/volc-sdk-golang v1.0.209/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= github.com/volcengine/volc-sdk-golang v1.0.211/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA= github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA=
github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@ -1688,8 +1688,8 @@ go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -1776,8 +1776,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1838,8 +1838,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1921,8 +1921,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1974,8 +1974,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -2139,8 +2139,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -2149,8 +2149,8 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -2222,8 +2222,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2298,8 +2298,8 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.234.0 h1:d3sAmYq3E9gdr2mpmiWGbm9pHsA/KJmyiLkwKfHBqU4= google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
google.golang.org/api v0.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg= google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -2444,8 +2444,8 @@ google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRx
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -2488,8 +2488,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -2567,8 +2567,8 @@ k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=

View file

@ -19,7 +19,6 @@ func RenewCert(w http.ResponseWriter, r *http.Request) {
conn, err := gpwebsocket.Initiate(w, r) conn, err := gpwebsocket.Initiate(w, r)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
defer conn.Close() defer conn.Close()

View file

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/yusing/go-proxy/agent/pkg/agent"
config "github.com/yusing/go-proxy/internal/config/types" config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
@ -43,7 +44,7 @@ func getDockerClients() (DockerClients, gperr.Error) {
dockerClients[name] = dockerClient dockerClients[name] = dockerClient
} }
for _, agent := range cfg.ListAgents() { for _, agent := range agent.ListAgents() {
dockerClient, err := docker.NewClient(agent.FakeDockerHost()) dockerClient, err := docker.NewClient(agent.FakeDockerHost())
if err != nil { if err != nil {
connErrs.Add(err) connErrs.Add(err)
@ -65,7 +66,7 @@ func getDockerClient(server string) (*docker.SharedClient, bool, error) {
} }
} }
if host == "" { if host == "" {
for _, agent := range cfg.ListAgents() { for _, agent := range agent.ListAgents() {
if agent.Name() == server { if agent.Name() == server {
host = agent.FakeDockerHost() host = agent.FakeDockerHost()
break break

View file

@ -44,6 +44,12 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
return return
} }
// try with alias
GetFavIconFromAlias(w, req, alias)
return
}
func GetFavIconFromAlias(w http.ResponseWriter, req *http.Request, alias string) {
// try with route.Icon // try with route.Icon
r, ok := routes.HTTP.Get(alias) r, ok := routes.HTTP.Get(alias)
if !ok { if !ok {

View file

@ -5,18 +5,18 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
config "github.com/yusing/go-proxy/internal/config/types" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket" "github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
) )
func ListAgents(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) { func ListAgents(w http.ResponseWriter, r *http.Request) {
if httpheaders.IsWebsocket(r.Header) { if httpheaders.IsWebsocket(r.Header) {
gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error { gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error {
return conn.WriteJSON(cfg.ListAgents()) return conn.WriteJSON(agent.ListAgents())
}) })
} else { } else {
gphttp.RespondJSON(w, r, cfg.ListAgents()) gphttp.RespondJSON(w, r, agent.ListAgents())
} }
} }

View file

@ -39,7 +39,7 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
return return
} }
hostport := fmt.Sprintf("%s:%d", host, port) hostport := fmt.Sprintf("%s:%d", host, port)
if _, ok := config.GetInstance().GetAgent(hostport); ok { if _, ok := agent.GetAgent(hostport); ok {
gphttp.KeyAlreadyExists(w, "agent", hostport) gphttp.KeyAlreadyExists(w, "agent", hostport)
return return
} }

View file

@ -4,16 +4,15 @@ import (
"net/http" "net/http"
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent" agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/metrics/systeminfo" "github.com/yusing/go-proxy/internal/metrics/systeminfo"
"github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) { func SystemInfo(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
agentAddr := query.Get("agent_addr") agentAddr := query.Get("agent_addr")
query.Del("agent_addr") query.Del("agent_addr")
@ -22,7 +21,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
return return
} }
agent, ok := cfg.GetAgent(agentAddr) agent, ok := agentPkg.GetAgent(agentAddr)
if !ok { if !ok {
gphttp.NotFound(w, "agent_addr") gphttp.NotFound(w, "agent_addr")
return return
@ -41,7 +40,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
} }
gphttp.WriteBody(w, respData) gphttp.WriteBody(w, respData)
} else { } else {
rp := reverseproxy.NewReverseProxy("agent", types.NewURL(agentPkg.AgentURL), agent.Transport()) rp := reverseproxy.NewReverseProxy("agent", nettypes.NewURL(agentPkg.AgentURL), agent.Transport())
header := r.Header.Clone() header := r.Header.Clone()
r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil) r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil)
if err != nil { if err != nil {

View file

@ -18,6 +18,7 @@ import (
"github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/utils" "github.com/yusing/go-proxy/internal/utils"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/time/rate"
) )
type ( type (
@ -162,6 +163,8 @@ func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
} }
} }
var rateLimit = rate.NewLimiter(rate.Every(time.Second), 1)
func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) { func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) {
// check for session token // check for session token
sessionToken, err := r.Cookie(CookieOauthSessionToken) sessionToken, err := r.Cookie(CookieOauthSessionToken)
@ -182,10 +185,21 @@ func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
if !rateLimit.Allow() {
http.Error(w, "auth rate limit exceeded", http.StatusTooManyRequests)
return
}
state := generateState() state := generateState()
SetTokenCookie(w, r, CookieOauthState, state, 300*time.Second) SetTokenCookie(w, r, CookieOauthState, state, 300*time.Second)
// redirect user to Idp // redirect user to Idp
http.Redirect(w, r, auth.oauthConfig.AuthCodeURL(state, optRedirectPostAuth(r)), http.StatusFound) url := auth.oauthConfig.AuthCodeURL(state, optRedirectPostAuth(r))
if IsFrontend(r) {
w.Header().Set("X-Redirect-To", url)
w.WriteHeader(http.StatusForbidden)
} else {
http.Redirect(w, r, url, http.StatusFound)
}
} }
func parseClaims(idToken *oidc.IDToken) (*IDTokenClaims, error) { func parseClaims(idToken *oidc.IDToken) (*IDTokenClaims, error) {

View file

@ -124,7 +124,8 @@ func (auth *UserPassAuth) PostAuthCallbackHandler(w http.ResponseWriter, r *http
} }
func (auth *UserPassAuth) LoginHandler(w http.ResponseWriter, r *http.Request) { func (auth *UserPassAuth) LoginHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/login", http.StatusFound) // redirects to WebUI login page w.Header().Set("X-Redirect-To", "/login")
w.WriteHeader(http.StatusForbidden)
} }
func (auth *UserPassAuth) LogoutHandler(w http.ResponseWriter, r *http.Request) { func (auth *UserPassAuth) LogoutHandler(w http.ResponseWriter, r *http.Request) {

View file

@ -1,6 +1,7 @@
package auth package auth
import ( import (
"net"
"net/http" "net/http"
"time" "time"
@ -16,7 +17,15 @@ var (
) )
func IsFrontend(r *http.Request) bool { func IsFrontend(r *http.Request) bool {
return r.Host == common.APIHTTPAddr return requestRemoteIP(r) == "127.0.0.1"
}
func requestRemoteIP(r *http.Request) string {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return ""
}
return ip
} }
func requestHost(r *http.Request) string { func requestHost(r *http.Request) string {

View file

@ -6,31 +6,8 @@ import (
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/route/provider" "github.com/yusing/go-proxy/internal/route/provider"
"github.com/yusing/go-proxy/internal/utils/functional"
) )
var agentPool = functional.NewMapOf[string, *agent.AgentConfig]()
func addAgent(agent *agent.AgentConfig) {
agentPool.Store(agent.Addr, agent)
}
func removeAllAgents() {
agentPool.Clear()
}
func GetAgent(addr string) (agent *agent.AgentConfig, ok bool) {
agent, ok = agentPool.Load(addr)
return
}
func (cfg *Config) GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool) {
if !agent.IsDockerHostAgent(agentAddrOrDockerHost) {
return GetAgent(agentAddrOrDockerHost)
}
return GetAgent(agent.GetAgentAddrFromDockerHost(agentAddrOrDockerHost))
}
func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) { func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) {
if slices.ContainsFunc(cfg.value.Providers.Agents, func(a *agent.AgentConfig) bool { if slices.ContainsFunc(cfg.value.Providers.Agents, func(a *agent.AgentConfig) bool {
return a.Addr == host return a.Addr == host
@ -44,23 +21,17 @@ func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PE
if err != nil { if err != nil {
return 0, gperr.Wrap(err, "failed to start agent") return 0, gperr.Wrap(err, "failed to start agent")
} }
addAgent(&agentCfg) agent.AddAgent(&agentCfg)
provider := provider.NewAgentProvider(&agentCfg) provider := provider.NewAgentProvider(&agentCfg)
if err := cfg.errIfExists(provider); err != nil { if err := cfg.errIfExists(provider); err != nil {
agent.RemoveAgent(&agentCfg)
return 0, err return 0, err
} }
err = provider.LoadRoutes() err = provider.LoadRoutes()
if err != nil { if err != nil {
agent.RemoveAgent(&agentCfg)
return 0, gperr.Wrap(err, "failed to load routes") return 0, gperr.Wrap(err, "failed to load routes")
} }
return provider.NumRoutes(), nil return provider.NumRoutes(), nil
} }
func (cfg *Config) ListAgents() []*agent.AgentConfig {
agents := make([]*agent.AgentConfig, 0, agentPool.Size())
agentPool.RangeAll(func(key string, value *agent.AgentConfig) {
agents = append(agents, value)
})
return agents
}

View file

@ -11,6 +11,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/api" "github.com/yusing/go-proxy/internal/api"
autocert "github.com/yusing/go-proxy/internal/autocert" autocert "github.com/yusing/go-proxy/internal/autocert"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
@ -117,13 +118,13 @@ func Reload() gperr.Error {
newCfg := newConfig() newCfg := newConfig()
err := newCfg.load() err := newCfg.load()
if err != nil { if err != nil {
newCfg.task.Finish(err) newCfg.task.FinishAndWait(err)
return gperr.New(ansi.Warning("using last config")).With(err) return gperr.New(ansi.Warning("using last config")).With(err)
} }
// cancel all current subtasks -> wait // cancel all current subtasks -> wait
// -> replace config -> start new subtasks // -> replace config -> start new subtasks
config.GetInstance().(*Config).Task().Finish("config changed") config.GetInstance().(*Config).Task().FinishAndWait("config changed")
newCfg.Start(StartAllServers) newCfg.Start(StartAllServers)
config.SetInstance(newCfg) config.SetInstance(newCfg)
return nil return nil
@ -323,14 +324,14 @@ func (cfg *Config) loadRouteProviders(providers *config.Providers) gperr.Error {
errs := gperr.NewBuilder("route provider errors") errs := gperr.NewBuilder("route provider errors")
results := gperr.NewBuilder("loaded route providers") results := gperr.NewBuilder("loaded route providers")
removeAllAgents() agentPkg.RemoveAllAgents()
for _, agent := range providers.Agents { for _, agent := range providers.Agents {
if err := agent.Start(cfg.task.Context()); err != nil { if err := agent.Start(cfg.task.Context()); err != nil {
errs.Add(gperr.PrependSubject(agent.String(), err)) errs.Add(gperr.PrependSubject(agent.String(), err))
continue continue
} }
addAgent(agent) agentPkg.AddAgent(agent)
p := proxy.NewAgentProvider(agent) p := proxy.NewAgentProvider(agent)
if err := cfg.errIfExists(p); err != nil { if err := cfg.errIfExists(p); err != nil {
errs.Add(err.Subject(p.String())) errs.Add(err.Subject(p.String()))

View file

@ -2,36 +2,25 @@ package config
import ( import (
config "github.com/yusing/go-proxy/internal/config/types" config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/provider" "github.com/yusing/go-proxy/internal/route/provider"
) )
func (cfg *Config) DumpRoutes() map[string]*route.Route {
entries := make(map[string]*route.Route)
cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
p.RangeRoutes(func(alias string, r *route.Route) {
entries[alias] = r
})
})
return entries
}
func (cfg *Config) DumpRouteProviders() map[string]*provider.Provider { func (cfg *Config) DumpRouteProviders() map[string]*provider.Provider {
entries := make(map[string]*provider.Provider) entries := make(map[string]*provider.Provider, cfg.providers.Size())
cfg.providers.RangeAll(func(_ string, p *provider.Provider) { for _, p := range cfg.providers.Range {
entries[p.ShortName()] = p entries[p.ShortName()] = p
}) }
return entries return entries
} }
func (cfg *Config) RouteProviderList() []config.RouteProviderListResponse { func (cfg *Config) RouteProviderList() []config.RouteProviderListResponse {
var list []config.RouteProviderListResponse list := make([]config.RouteProviderListResponse, 0, cfg.providers.Size())
cfg.providers.RangeAll(func(_ string, p *provider.Provider) { for _, p := range cfg.providers.Range {
list = append(list, config.RouteProviderListResponse{ list = append(list, config.RouteProviderListResponse{
ShortName: p.ShortName(), ShortName: p.ShortName(),
FullName: p.String(), FullName: p.String(),
}) })
}) }
return list return list
} }
@ -40,13 +29,13 @@ func (cfg *Config) Statistics() map[string]any {
var total uint16 var total uint16
providerStats := make(map[string]provider.ProviderStats) providerStats := make(map[string]provider.ProviderStats)
cfg.providers.RangeAll(func(_ string, p *provider.Provider) { for _, p := range cfg.providers.Range {
stats := p.Statistics() stats := p.Statistics()
providerStats[p.ShortName()] = stats providerStats[p.ShortName()] = stats
rps.AddOther(stats.RPs) rps.AddOther(stats.RPs)
streams.AddOther(stats.Streams) streams.AddOther(stats.Streams)
total += stats.RPs.Total + stats.Streams.Total total += stats.RPs.Total + stats.Streams.Total
}) }
return map[string]any{ return map[string]any{
"total": total, "total": total,

View file

@ -52,9 +52,7 @@ type (
Statistics() map[string]any Statistics() map[string]any
RouteProviderList() []RouteProviderListResponse RouteProviderList() []RouteProviderListResponse
Context() context.Context Context() context.Context
GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool)
VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error)
ListAgents() []*agent.AgentConfig
AutoCertProvider() *autocert.Provider AutoCertProvider() *autocert.Provider
} }
) )

View file

@ -1,6 +1,6 @@
module github.com/yusing/go-proxy/internal/dnsproviders module github.com/yusing/go-proxy/internal/dnsproviders
go 1.24.3 go 1.24.4
replace github.com/yusing/go-proxy => ../.. replace github.com/yusing/go-proxy => ../..
@ -8,16 +8,16 @@ replace github.com/yusing/go-proxy/internal/utils => ../utils
require ( require (
github.com/go-acme/lego/v4 v4.23.1 github.com/go-acme/lego/v4 v4.23.1
github.com/yusing/go-proxy v0.0.0-00010101000000-000000000000 github.com/yusing/go-proxy v0.14.2
) )
require ( require (
cloud.google.com/go/auth v0.16.1 // indirect cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
@ -26,30 +26,30 @@ require (
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.16 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.69 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 // indirect github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 // indirect
github.com/aws/smithy-go v1.22.3 // indirect github.com/aws/smithy-go v1.22.3 // indirect
github.com/baidubce/bce-sdk-go v0.9.228 // indirect github.com/baidubce/bce-sdk-go v0.9.230 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect github.com/benbjohnson/clock v1.3.5 // indirect
github.com/boombuler/barcode v1.0.2 // indirect github.com/boombuler/barcode v1.0.2 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/civo/civogo v0.5.3 // indirect github.com/civo/civogo v0.6.1 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dnsimple/dnsimple-go v1.7.0 // indirect github.com/dnsimple/dnsimple-go v1.7.0 // indirect
github.com/exoscale/egoscale/v3 v3.1.18 // indirect github.com/exoscale/egoscale/v3 v3.1.20 // indirect
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
@ -57,7 +57,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/go-errors/errors v1.5.1 // indirect github.com/go-errors/errors v1.5.1 // indirect
github.com/go-jose/go-jose/v4 v4.1.0 // indirect github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
@ -65,7 +65,7 @@ require (
github.com/go-resty/resty/v2 v2.16.5 // indirect github.com/go-resty/resty/v2 v2.16.5 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.17.1 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
@ -80,7 +80,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
@ -91,7 +91,7 @@ require (
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect github.com/labbsr0x/goh v1.0.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/linode/linodego v1.51.0 // indirect github.com/linode/linodego v1.52.1 // indirect
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
github.com/liquidweb/liquidweb-go v1.6.4 // indirect github.com/liquidweb/liquidweb-go v1.6.4 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
@ -115,7 +115,7 @@ require (
github.com/nrdcg/porkbun v0.4.0 // indirect github.com/nrdcg/porkbun v0.4.0 // indirect
github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/nzdjb/go-metaname v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v65 v65.91.1 // indirect github.com/oracle/oci-go-sdk/v65 v65.93.1 // indirect
github.com/ovh/go-ovh v1.7.0 // indirect github.com/ovh/go-ovh v1.7.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
@ -129,7 +129,7 @@ require (
github.com/rs/zerolog v1.34.0 // indirect github.com/rs/zerolog v1.34.0 // indirect
github.com/sacloud/api-client-go v0.3.0 // indirect github.com/sacloud/api-client-go v0.3.0 // indirect
github.com/sacloud/go-http v0.1.9 // indirect github.com/sacloud/go-http v0.1.9 // indirect
github.com/sacloud/iaas-api-go v1.15.0 // indirect github.com/sacloud/iaas-api-go v1.16.0 // indirect
github.com/sacloud/packages-go v0.0.11 // indirect github.com/sacloud/packages-go v0.0.11 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
@ -143,23 +143,23 @@ require (
github.com/sony/gobreaker v1.0.0 // indirect github.com/sony/gobreaker v1.0.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.14.0 // indirect github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.8.0 // indirect github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.20.1 // indirect github.com/spf13/viper v1.20.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect github.com/stretchr/testify v1.10.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1170 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/transip/gotransip/v6 v6.26.0 // indirect github.com/transip/gotransip/v6 v6.26.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/volcengine/volc-sdk-golang v1.0.209 // indirect github.com/volcengine/volc-sdk-golang v1.0.211 // indirect
github.com/vultr/govultr/v3 v3.20.0 // indirect github.com/vultr/govultr/v3 v3.20.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/yusing/go-proxy/internal/utils v0.0.0 // indirect github.com/yusing/go-proxy/internal/utils v0.0.0 // indirect
go.mongodb.org/mongo-driver v1.17.3 // indirect go.mongodb.org/mongo-driver v1.17.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect
@ -168,18 +168,18 @@ require (
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/ratelimit v0.3.1 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/crypto v0.39.0 // indirect
golang.org/x/mod v0.24.0 // indirect golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.40.0 // indirect golang.org/x/net v0.41.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.11.0 // indirect golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.34.0 // indirect
google.golang.org/api v0.234.0 // indirect google.golang.org/api v0.237.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.72.1 // indirect google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
@ -189,7 +189,7 @@ require (
k8s.io/api v0.33.1 // indirect k8s.io/api v0.33.1 // indirect
k8s.io/apimachinery v0.33.1 // indirect k8s.io/apimachinery v0.33.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect

View file

@ -97,8 +97,8 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo
cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo=
cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
@ -606,8 +606,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesn
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
@ -670,40 +670,40 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= github.com/aws/aws-sdk-go-v2/config v1.29.16 h1:XkruGnXX1nEZ+Nyo9v84TzsX+nj86icbFAeust6uo8A=
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= github.com/aws/aws-sdk-go-v2/config v1.29.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= github.com/aws/aws-sdk-go-v2/credentials v1.17.69/go.mod h1:gPME6I8grR1jCqBFEGthULiolzf/Sexq/Wy42ibKK9c=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 h1:/ldKrPPXTC421bTNWrUIpq3CxwHwRI/kpc+jPUTJocM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 h1:8NcRiAZn54D1W6rQZaZfZtjnLH07WciOhDcYIKpVKQM=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2/go.mod h1:Qy22QnQSdHbZwMZrarsWZBIuK51isPlkD+Z4sztxX0o= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3/go.mod h1:nramkVwrN/StFgVfTZp6Azi1J4lWnq+99fwpJRTasj4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 h1:41HrH51fydStW2Tah74zkqZlJfyx4gXeuGOdsIFuckY= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 h1:PAbznrQ8b8IwTUJgBdcbVqc+r57SO3jy0YJi9bJKPmQ=
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1/go.mod h1:cpFFGJ0A6WKZjf26TVzYI3qFhbFXXb7xeF5bOOMax6c=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 h1:EU58LP8ozQDVroOEyAfcq0cGc5R/FTZjVoYJ6tvby3w=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 h1:XB4z0hbQtpmBnb1FQYvKaCM7UsS6Y/u8jVBwIUGeCTk=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 h1:nyLjs8sYJShFYj6aiyjCBI3EcLn1udWrQTjEF+SOXB0=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/baidubce/bce-sdk-go v0.9.228 h1:XEY3/oAxXcsi7+3atib9fMI6YNE9sL5qo+WMZ+iqmNE= github.com/baidubce/bce-sdk-go v0.9.230 h1:HzELBKiD7QAgYqZ1qHZexoI2A3Lo/6zYGQFvcUbS5cA=
github.com/baidubce/bce-sdk-go v0.9.228/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/baidubce/bce-sdk-go v0.9.230/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -739,8 +739,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/civo/civogo v0.5.3 h1:FLtiosmNxHErTPonCouosPmg9C+3itmI/qxNxuPQIZs= github.com/civo/civogo v0.6.1 h1:PFOh7rBU0vmj7LTDIv3z7l9uXG4SZyyzScCl3wyTFSc=
github.com/civo/civogo v0.5.3/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.6.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
@ -801,8 +801,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/exoscale/egoscale/v3 v3.1.18 h1:9LvH61Cd0L/JC04KartbICl/hVva5AAwtTzzOxLtSYA= github.com/exoscale/egoscale/v3 v3.1.20 h1:AZw7VWblEM4+77bjFt9pHBvOQj+spqLkDVxGxK7cbUU=
github.com/exoscale/egoscale/v3 v3.1.18/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/exoscale/egoscale/v3 v3.1.20/go.mod h1:A53enXfm8nhVMpIYw0QxiwQ2P6AdCF4F/nVYChNEzdE=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
@ -861,8 +861,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
@ -890,8 +890,8 @@ github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVr
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
@ -992,8 +992,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE=
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
@ -1100,8 +1100,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 h1:Ih+z79Ko1ClH4dlv7O1lyHRiVCjkb2NZYYk+1cSZbU8= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 h1:vqrLDi/UZpBEliQXLfdavmtETgGLj82iU8Tja0URzqI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -1181,8 +1181,8 @@ github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/linode/linodego v1.51.0 h1:8S0vNQRUZAuumGFe8h/huCzbyExxlvOUR2Uf0oDoJ/8= github.com/linode/linodego v1.52.1 h1:HJ1cz1n9n3chRP9UrtqmP91+xTi0Q5l+H/4z4tpkwgQ=
github.com/linode/linodego v1.51.0/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw= github.com/linode/linodego v1.52.1/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw=
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
@ -1322,8 +1322,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oracle/oci-go-sdk/v65 v65.91.1 h1:jRE4jUiJd+sDhJTyTCXIJdzLjTx+99gA3PgzpVTaF/I= github.com/oracle/oci-go-sdk/v65 v65.93.1 h1:lIvy/6aQOUenQI+cxXH1wDBJeXFPO9Du3CaomXeYFaY=
github.com/oracle/oci-go-sdk/v65 v65.91.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.93.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@ -1422,8 +1422,8 @@ github.com/sacloud/api-client-go v0.3.0 h1:NLA2BpZ5welAXBPxgmXSCjgn/k0ShDB0x5kmU
github.com/sacloud/api-client-go v0.3.0/go.mod h1:v8ke3pxVQ9TCWEbMkfbLX8NMeGiPpE+uDwlgcUWfMgM= github.com/sacloud/api-client-go v0.3.0/go.mod h1:v8ke3pxVQ9TCWEbMkfbLX8NMeGiPpE+uDwlgcUWfMgM=
github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE= github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE= github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE=
github.com/sacloud/iaas-api-go v1.15.0 h1:G88U69OOVUSjOZ2fEP5QWMg7r3VOVsfNkl5Mxl+/wMk= github.com/sacloud/iaas-api-go v1.16.0 h1:JUj7f5yHSSakhy0N8qsZ0a5IYpbUgsv4x5rN8pgRbEg=
github.com/sacloud/iaas-api-go v1.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA= github.com/sacloud/iaas-api-go v1.16.0/go.mod h1:8BTuDTCTT6Ie08544+q5hT3GMyTf2DDxzt/XMuwmhgk=
github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo= github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo=
github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
@ -1476,8 +1476,8 @@ github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZ
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@ -1518,11 +1518,11 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1170/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1184/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172 h1:cfh0m0esFTjBZ6YZRCVWUDs772KOfxQR/mKY7zL8/qg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 h1:LlFJcouP8DMN4tLWfF9oYG+x4tzd40ISMMZ8MaGNH80=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1170 h1:qdjh5LP8N2bE8cPfBX6Y3S/3+5yD2JLJBx7EALVrgP0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 h1:nZ6YVgr1X332hEI8bQEyW4Hhp9FFOYEZ6Q8fSxND2pQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1170/go.mod h1:OS8GJ/x+p/6Wchc6Lp6iA9Gf9sQ/it0xatH1AXGX8PY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184/go.mod h1:nigwfh7wyX/yj24MdEEq++yvsPp1XkSczazhb0wzYts=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -1538,8 +1538,8 @@ github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/volcengine/volc-sdk-golang v1.0.209 h1:aYCVO2fQ4meqLZ+XjVLcKzDn7kYfrUekqp48iioH3ug= github.com/volcengine/volc-sdk-golang v1.0.211 h1:FgwD+1phyy+un4Qk2YqooYtp6XpvNDQ4a/fpsCAtDHo=
github.com/volcengine/volc-sdk-golang v1.0.209/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= github.com/volcengine/volc-sdk-golang v1.0.211/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA= github.com/vultr/govultr/v3 v3.20.0 h1:O+Om6gXpN6ehwAIIKq5DyGuekpyHaoRlwrxTb44bDzA=
github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/vultr/govultr/v3 v3.20.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@ -1577,8 +1577,8 @@ go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -1655,8 +1655,8 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1714,8 +1714,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1794,8 +1794,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1843,8 +1843,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1998,8 +1998,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -2008,8 +2008,8 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -2079,8 +2079,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2155,8 +2155,8 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.234.0 h1:d3sAmYq3E9gdr2mpmiWGbm9pHsA/KJmyiLkwKfHBqU4= google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
google.golang.org/api v0.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg= google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -2301,8 +2301,8 @@ google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRx
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -2345,8 +2345,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -2422,8 +2422,8 @@ k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=

View file

@ -16,7 +16,6 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
) )
@ -58,20 +57,16 @@ func initClientCleaner() {
case <-ticker.C: case <-ticker.C:
closeTimedOutClients() closeTimedOutClients()
case <-cleaner.Context().Done(): case <-cleaner.Context().Done():
clientMapMu.Lock()
for _, c := range clientMap {
delete(clientMap, c.Key())
c.Client.Close()
}
clientMapMu.Unlock()
return return
} }
} }
}() }()
task.OnProgramExit("docker_clients_cleanup", func() {
clientMapMu.Lock()
defer clientMapMu.Unlock()
for _, c := range clientMap {
delete(clientMap, c.Key())
c.Client.Close()
}
})
} }
func closeTimedOutClients() { func closeTimedOutClients() {
@ -126,7 +121,7 @@ func NewClient(host string) (*SharedClient, error) {
var dial func(ctx context.Context) (net.Conn, error) var dial func(ctx context.Context) (net.Conn, error)
if agent.IsDockerHostAgent(host) { if agent.IsDockerHostAgent(host) {
cfg, ok := config.GetInstance().GetAgent(host) cfg, ok := agent.GetAgent(host)
if !ok { if !ok {
panic(fmt.Errorf("agent %q not found", host)) panic(fmt.Errorf("agent %q not found", host))
} }

View file

@ -2,6 +2,8 @@ package docker
import ( import (
"context" "context"
"errors"
"fmt"
"net" "net"
"net/url" "net/url"
"strconv" "strconv"
@ -9,9 +11,7 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
"github.com/yusing/go-proxy/internal/serialization" "github.com/yusing/go-proxy/internal/serialization"
@ -35,6 +35,7 @@ type (
Mounts []string `json:"mounts"` Mounts []string `json:"mounts"`
Network string `json:"network,omitempty"`
PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port
PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port
PublicHostname string `json:"public_hostname"` PublicHostname string `json:"public_hostname"`
@ -45,6 +46,8 @@ type (
IsExplicit bool `json:"is_explicit"` IsExplicit bool `json:"is_explicit"`
IsHostNetworkMode bool `json:"is_host_network_mode"` IsHostNetworkMode bool `json:"is_host_network_mode"`
Running bool `json:"running"` Running bool `json:"running"`
Errors *containerError `json:"errors"`
} }
ContainerImage struct { ContainerImage struct {
Author string `json:"author,omitempty"` Author string `json:"author,omitempty"`
@ -55,16 +58,24 @@ type (
var DummyContainer = new(Container) var DummyContainer = new(Container)
var (
ErrNetworkNotFound = errors.New("network not found")
ErrNoNetwork = errors.New("no network found")
)
func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container) { func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container) {
isExplicit := false _, isExplicit := c.Labels[LabelAliases]
helper := containerHelper{c} helper := containerHelper{c}
for lbl := range c.Labels { if !isExplicit {
if strings.HasPrefix(lbl, NSProxy+".") { // walk through all labels to check if any label starts with NSProxy.
isExplicit = true for lbl := range c.Labels {
} else { if strings.HasPrefix(lbl, NSProxy+".") {
delete(c.Labels, lbl) isExplicit = true
break
}
} }
} }
network := helper.getDeleteLabel(LabelNetwork)
isExcluded, _ := strconv.ParseBool(helper.getDeleteLabel(LabelExclude)) isExcluded, _ := strconv.ParseBool(helper.getDeleteLabel(LabelExclude))
res = &Container{ res = &Container{
@ -77,6 +88,7 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
Mounts: helper.getMounts(), Mounts: helper.getMounts(),
Network: network,
PublicPortMapping: helper.getPublicPortMapping(), PublicPortMapping: helper.getPublicPortMapping(),
PrivatePortMapping: helper.getPrivatePortMapping(), PrivatePortMapping: helper.getPrivatePortMapping(),
@ -89,15 +101,19 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
if agent.IsDockerHostAgent(dockerHost) { if agent.IsDockerHostAgent(dockerHost) {
var ok bool var ok bool
res.Agent, ok = config.GetInstance().GetAgent(dockerHost) res.Agent, ok = agent.GetAgent(dockerHost)
if !ok { if !ok {
log.Error().Msgf("agent %q not found", dockerHost) res.addError(fmt.Errorf("agent %q not found", dockerHost))
} }
} }
res.setPrivateHostname(helper) res.setPrivateHostname(helper)
res.setPublicHostname() res.setPublicHostname()
res.loadDeleteIdlewatcherLabels(helper) res.loadDeleteIdlewatcherLabels(helper)
if res.PrivateHostname == "" && res.PublicHostname == "" && res.Running {
res.addError(ErrNoNetwork)
}
return return
} }
@ -124,14 +140,30 @@ func (c *Container) UpdatePorts() error {
continue continue
} }
c.PublicPortMapping[portInt] = container.Port{ c.PublicPortMapping[portInt] = container.Port{
PublicPort: uint16(portInt), PublicPort: uint16(portInt), //nolint:gosec
PrivatePort: uint16(portInt), PrivatePort: uint16(portInt), //nolint:gosec
Type: proto, Type: proto,
} }
} }
return nil return nil
} }
func (c *Container) DockerComposeProject() string {
return c.Labels["com.docker.compose.project"]
}
func (c *Container) DockerComposeService() string {
return c.Labels["com.docker.compose.service"]
}
func (c *Container) Dependencies() []string {
deps := c.Labels[LabelDependsOn]
if deps == "" {
deps = c.Labels["com.docker.compose.depends_on"]
}
return strings.Split(deps, ",")
}
var databaseMPs = map[string]struct{}{ var databaseMPs = map[string]struct{}{
"/var/lib/postgresql/data": {}, "/var/lib/postgresql/data": {},
"/var/lib/mysql": {}, "/var/lib/mysql": {},
@ -184,7 +216,6 @@ func (c *Container) setPublicHostname() {
} }
url, err := url.Parse(c.DockerHost) url, err := url.Parse(c.DockerHost)
if err != nil { if err != nil {
log.Err(err).Msgf("invalid docker host %q, falling back to 127.0.0.1", c.DockerHost)
c.PublicHostname = "127.0.0.1" c.PublicHostname = "127.0.0.1"
return return
} }
@ -198,8 +229,32 @@ func (c *Container) setPrivateHostname(helper containerHelper) {
if helper.NetworkSettings == nil { if helper.NetworkSettings == nil {
return return
} }
for _, v := range helper.NetworkSettings.Networks { if c.Network != "" {
v, ok := helper.NetworkSettings.Networks[c.Network]
if ok {
c.PrivateHostname = v.IPAddress
return
}
// try {project_name}_{network_name}
if proj := c.DockerComposeProject(); proj != "" {
oldNetwork, newNetwork := c.Network, fmt.Sprintf("%s_%s", proj, c.Network)
if newNetwork != oldNetwork {
v, ok = helper.NetworkSettings.Networks[newNetwork]
if ok {
c.Network = newNetwork // update network to the new one
c.PrivateHostname = v.IPAddress
return
}
}
}
nearest := gperr.DoYouMean(utils.NearestField(c.Network, helper.NetworkSettings.Networks))
c.addError(fmt.Errorf("network %q not found, %w", c.Network, nearest))
return
}
// fallback to first network if no network is specified
for k, v := range helper.NetworkSettings.Networks {
if v.IPAddress != "" { if v.IPAddress != "" {
c.Network = k // update network to the first network
c.PrivateHostname = v.IPAddress c.PrivateHostname = v.IPAddress
return return
} }
@ -214,22 +269,34 @@ func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
"stop_timeout": helper.getDeleteLabel(LabelStopTimeout), "stop_timeout": helper.getDeleteLabel(LabelStopTimeout),
"stop_signal": helper.getDeleteLabel(LabelStopSignal), "stop_signal": helper.getDeleteLabel(LabelStopSignal),
"start_endpoint": helper.getDeleteLabel(LabelStartEndpoint), "start_endpoint": helper.getDeleteLabel(LabelStartEndpoint),
"depends_on": c.Dependencies(),
} }
// ensure it's deleted from labels
helper.getDeleteLabel(LabelDependsOn)
// set only if idlewatcher is enabled // set only if idlewatcher is enabled
idleTimeout := cfg["idle_timeout"] idleTimeout := cfg["idle_timeout"]
if idleTimeout != "" { if idleTimeout != "" {
idwCfg := &idlewatcher.Config{ idwCfg := new(idlewatcher.Config)
Docker: &idlewatcher.DockerConfig{ idwCfg.Docker = &idlewatcher.DockerConfig{
DockerHost: c.DockerHost, DockerHost: c.DockerHost,
ContainerID: c.ContainerID, ContainerID: c.ContainerID,
ContainerName: c.ContainerName, ContainerName: c.ContainerName,
},
} }
err := serialization.MapUnmarshalValidate(cfg, idwCfg) err := serialization.MapUnmarshalValidate(cfg, idwCfg)
if err != nil { if err != nil {
gperr.LogWarn("invalid idlewatcher config", gperr.PrependSubject(c.ContainerName, err)) c.addError(err)
} else { } else {
c.IdlewatcherConfig = idwCfg c.IdlewatcherConfig = idwCfg
} }
} }
} }
func (c *Container) addError(err error) {
if c.Errors == nil {
c.Errors = new(containerError)
}
c.Errors.Add(err)
}

View file

@ -48,10 +48,13 @@ func (c containerHelper) parseImage() *ContainerImage {
im.Author = strings.Join(slashSep[:len(slashSep)-1], "/") im.Author = strings.Join(slashSep[:len(slashSep)-1], "/")
im.Name = slashSep[len(slashSep)-1] im.Name = slashSep[len(slashSep)-1]
} else { } else {
im.Author = "library"
im.Name = slashSep[0] im.Name = slashSep[0]
} }
if len(colonSep) > 1 { if len(colonSep) > 1 {
im.Tag = colonSep[1] im.Tag = colonSep[1]
} else {
im.Tag = "latest"
} }
return im return im
} }

View file

@ -76,3 +76,40 @@ func TestContainerHostNetworkMode(t *testing.T) {
}) })
} }
} }
func TestImageNameParsing(t *testing.T) {
tests := []struct {
full string
author string
image string
tag string
}{
{
full: "ghcr.io/tensorchord/pgvecto-rs",
author: "ghcr.io/tensorchord",
image: "pgvecto-rs",
tag: "latest",
},
{
full: "redis:latest",
author: "library",
image: "redis",
tag: "latest",
},
{
full: "redis:7.4.0-alpine",
author: "library",
image: "redis",
tag: "7.4.0-alpine",
},
}
for _, tt := range tests {
t.Run(tt.full, func(t *testing.T) {
helper := containerHelper{&container.SummaryTrimmed{Image: tt.full}}
im := helper.parseImage()
ExpectEqual(t, im.Author, tt.author)
ExpectEqual(t, im.Name, tt.image)
ExpectEqual(t, im.Tag, tt.tag)
})
}
}

34
internal/docker/errors.go Normal file
View file

@ -0,0 +1,34 @@
package docker
import (
"encoding/json"
"github.com/yusing/go-proxy/internal/gperr"
)
type 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()))
}

View file

@ -1,6 +1,10 @@
package docker package docker
import ( import (
"fmt"
"strings"
"github.com/goccy/go-yaml"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -9,10 +13,12 @@ type LabelMap = map[string]any
var ErrInvalidLabel = gperr.New("invalid label") var ErrInvalidLabel = gperr.New("invalid label")
func ParseLabels(labels map[string]string) (LabelMap, gperr.Error) { func ParseLabels(labels map[string]string, aliases ...string) (LabelMap, gperr.Error) {
nestedMap := make(LabelMap) nestedMap := make(LabelMap)
errs := gperr.NewBuilder("labels error") errs := gperr.NewBuilder("labels error")
ExpandWildcard(labels, aliases...)
for lbl, value := range labels { for lbl, value := range labels {
parts := strutils.SplitRune(lbl, '.') parts := strutils.SplitRune(lbl, '.')
if parts[0] != NSProxy { if parts[0] != NSProxy {
@ -50,3 +56,93 @@ func ParseLabels(labels map[string]string) (LabelMap, gperr.Error) {
return nestedMap, errs.Error() return nestedMap, errs.Error()
} }
func ExpandWildcard(labels map[string]string, aliases ...string) {
// collect all explicit aliases first
aliasSet := make(map[string]struct{}, len(labels))
// wildcardLabels holds mapping suffix -> value derived from wildcard label definitions
wildcardLabels := make(map[string]string)
for _, alias := range aliases {
aliasSet[alias] = struct{}{}
}
// iterate over a copy of the keys to safely mutate the map while ranging
for lbl, value := range labels {
parts := strings.SplitN(lbl, ".", 3)
if len(parts) < 2 || parts[0] != NSProxy {
continue
}
alias := parts[1]
if alias == WildcardAlias { // "*"
// remove wildcard label from original map it should not remain afterwards
delete(labels, lbl)
// value looks like YAML (multiline)
if strings.Count(value, "\n") > 1 {
expandYamlWildcard(value, wildcardLabels)
continue
}
// normal wildcard label with suffix store directly
wildcardLabels[parts[2]] = value
continue
}
// explicit alias label remember the alias
aliasSet[alias] = struct{}{}
}
if len(aliasSet) == 0 || len(wildcardLabels) == 0 {
return // nothing to expand
}
// expand collected wildcard labels for every alias
for suffix, v := range wildcardLabels {
for alias := range aliasSet {
key := fmt.Sprintf("%s.%s.%s", NSProxy, alias, suffix)
if suffix == "" { // this should not happen (root wildcard handled earlier) but keep safe
key = fmt.Sprintf("%s.%s", NSProxy, alias)
}
labels[key] = v
}
}
}
// expandYamlWildcard parses a YAML document in value, flattens it to dot-notated keys and adds the
// results into dest map where each key is the flattened suffix and the value is the scalar string
// representation. The provided YAML is expected to be a mapping.
func expandYamlWildcard(value string, dest map[string]string) {
// replace tab indentation with spaces to make YAML parser happy
yamlStr := strings.ReplaceAll(value, "\t", " ")
raw := make(map[string]any)
if err := yaml.Unmarshal([]byte(yamlStr), &raw); err != nil {
// on parse error, ignore treat as no-op
return
}
flattenMap("", raw, dest)
}
// flattenMap converts nested maps into a flat map with dot-delimited keys.
func flattenMap(prefix string, src map[string]any, dest map[string]string) {
for k, v := range src {
key := k
if prefix != "" {
key = prefix + "." + k
}
switch vv := v.(type) {
case map[string]any:
flattenMap(key, vv, dest)
case map[any]any:
// convert to map[string]any by stringifying keys
tmp := make(map[string]any, len(vv))
for kk, vvv := range vv {
tmp[fmt.Sprintf("%v", kk)] = vvv
}
flattenMap(key, tmp, dest)
default:
dest[key] = fmt.Sprint(v)
}
}
}

View file

@ -3,16 +3,63 @@ package docker_test
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require"
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
) )
func TestExpandWildcard(t *testing.T) {
labels := map[string]string{
"proxy.a.host": "localhost",
"proxy.b.port": "4444",
"proxy.b.scheme": "http",
"proxy.*.port": "5555",
"proxy.*.healthcheck.disable": "true",
}
docker.ExpandWildcard(labels)
require.Equal(t, map[string]string{
"proxy.a.host": "localhost",
"proxy.a.port": "5555",
"proxy.a.healthcheck.disable": "true",
"proxy.b.scheme": "http",
"proxy.b.port": "5555",
"proxy.b.healthcheck.disable": "true",
}, labels)
}
func TestExpandWildcardYAML(t *testing.T) {
yaml := `
host: localhost
port: 5555
healthcheck:
disable: true`
labels := map[string]string{
"proxy.*": yaml[1:],
"proxy.a.port": "4444",
"proxy.a.healthcheck.disable": "false",
"proxy.a.healthcheck.path": "/health",
"proxy.b.port": "6666",
}
docker.ExpandWildcard(labels)
require.Equal(t, map[string]string{
"proxy.a.host": "localhost", // set by wildcard
"proxy.a.port": "5555", // overridden by wildcard
"proxy.a.healthcheck.disable": "true", // overridden by wildcard
"proxy.a.healthcheck.path": "/health", // own label
"proxy.b.host": "localhost", // set by wildcard
"proxy.b.port": "5555", // overridden by wildcard
"proxy.b.healthcheck.disable": "true", // overridden by wildcard
}, labels)
}
func BenchmarkParseLabels(b *testing.B) { func BenchmarkParseLabels(b *testing.B) {
for range b.N { for b.Loop() {
_, _ = docker.ParseLabels(map[string]string{ _, _ = docker.ParseLabels(map[string]string{
"proxy.a.host": "localhost", "proxy.a.host": "localhost",
"proxy.a.port": "4444", "proxy.b.port": "4444",
"proxy.a.scheme": "http", "proxy.*.scheme": "http",
"proxy.a.middlewares.request.hide_headers": "X-Header1,X-Header2", "proxy.*.middlewares.request.hide_headers": "X-Header1,X-Header2",
}) })
} }
} }

View file

@ -13,4 +13,6 @@ const (
LabelStopTimeout = NSProxy + ".stop_timeout" LabelStopTimeout = NSProxy + ".stop_timeout"
LabelStopSignal = NSProxy + ".stop_signal" LabelStopSignal = NSProxy + ".stop_signal"
LabelStartEndpoint = NSProxy + ".start_endpoint" LabelStartEndpoint = NSProxy + ".start_endpoint"
LabelDependsOn = NSProxy + ".depends_on"
LabelNetwork = NSProxy + ".network"
) )

View file

@ -3,7 +3,7 @@ package homepage
import ( import (
"net/http" "net/http"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/pool" "github.com/yusing/go-proxy/internal/utils/pool"
) )
@ -11,7 +11,7 @@ type route interface {
pool.Object pool.Object
ProviderName() string ProviderName() string
References() []string References() []string
TargetURL() *gpnet.URL TargetURL() *nettypes.URL
} }
type httpRoute interface { type httpRoute interface {

View file

@ -1,8 +1,10 @@
package idlewatcher package idlewatcher
import "context" import (
"context"
)
func (w *Watcher) cancelled(reqCtx context.Context) bool { func (w *Watcher) canceled(reqCtx context.Context) bool {
select { select {
case <-reqCtx.Done(): case <-reqCtx.Done():
w.l.Debug().AnErr("cause", context.Cause(reqCtx)).Msg("wake canceled") w.l.Debug().AnErr("cause", context.Cause(reqCtx)).Msg("wake canceled")
@ -11,3 +13,12 @@ func (w *Watcher) cancelled(reqCtx context.Context) bool {
return false return false
} }
} }
func (w *Watcher) waitStarted(reqCtx context.Context) bool {
select {
case <-reqCtx.Done():
return false
case <-w.route.Started():
return true
}
}

View file

@ -39,3 +39,10 @@ func Watchers() iter.Seq2[string, watcherDebug] {
} }
} }
} }
func fmtErr(err error) string {
if err == nil {
return ""
}
return err.Error()
}

View file

@ -0,0 +1,65 @@
package idlewatcher
import (
"context"
"errors"
"fmt"
)
type watcherError struct {
watcher *Watcher
err error
}
func (e *watcherError) Unwrap() error {
return e.err
}
func (e *watcherError) Error() string {
return fmt.Sprintf("watcher %q error: %s", e.watcher.cfg.ContainerName(), e.err.Error())
}
func (w *Watcher) newWatcherError(err error) error {
if errors.Is(err, causeReload) {
return nil
}
if wErr, ok := err.(*watcherError); ok { //nolint:errorlint
return wErr
}
return &watcherError{watcher: w, err: convertError(err)}
}
type depError struct {
action string
dep *dependency
err error
}
func (e *depError) Unwrap() error {
return e.err
}
func (e *depError) Error() string {
return fmt.Sprintf("%s failed for dependency %q: %s", e.action, e.dep.cfg.ContainerName(), e.err.Error())
}
func (w *Watcher) newDepError(action string, dep *dependency, err error) error {
if errors.Is(err, causeReload) {
return nil
}
if dErr, ok := err.(*depError); ok { //nolint:errorlint
return dErr
}
return w.newWatcherError(&depError{action: action, dep: dep, err: convertError(err)})
}
func convertError(err error) error {
switch {
case err == nil:
return nil
case errors.Is(err, context.DeadlineExceeded):
return errors.New("timeout")
default:
return err
}
}

View file

@ -1,8 +1,6 @@
package idlewatcher package idlewatcher
import ( import (
"context"
"errors"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
@ -38,10 +36,6 @@ func (w *Watcher) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
return return
default: default:
f := &ForceCacheControl{expires: w.expires().Format(http.TimeFormat), ResponseWriter: rw} f := &ForceCacheControl{expires: w.expires().Format(http.TimeFormat), ResponseWriter: rw}
w, ok := watcherMap[w.Key()] // could've been reloaded
if !ok {
return
}
w.rp.ServeHTTP(f, r) w.rp.ServeHTTP(f, r)
} }
} }
@ -50,6 +44,14 @@ func isFaviconPath(path string) bool {
return path == "/favicon.ico" return path == "/favicon.ico"
} }
func (w *Watcher) redirectToStartEndpoint(rw http.ResponseWriter, r *http.Request) {
uri := "/"
if w.cfg.StartEndpoint != "" {
uri = w.cfg.StartEndpoint
}
http.Redirect(rw, r, uri, http.StatusTemporaryRedirect)
}
func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldNext bool) { func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldNext bool) {
w.resetIdleTimer() w.resetIdleTimer()
@ -60,8 +62,7 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
// handle favicon request // handle favicon request
if isFaviconPath(r.URL.Path) { if isFaviconPath(r.URL.Path) {
r.URL.RawQuery = "alias=" + w.rp.TargetName favicon.GetFavIconFromAlias(rw, r, w.route.Name())
favicon.GetFavIcon(rw, r)
return false return false
} }
@ -90,32 +91,32 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
return false return false
} }
ctx, cancel := context.WithTimeoutCause(r.Context(), w.cfg.WakeTimeout, errors.New("wake timeout")) ctx := r.Context()
defer cancel() if w.canceled(ctx) {
w.redirectToStartEndpoint(rw, r)
if w.cancelled(ctx) {
gphttp.ServerError(rw, r, context.Cause(ctx), http.StatusServiceUnavailable)
return false return false
} }
w.l.Trace().Msg("signal received") w.l.Trace().Msg("signal received")
err := w.wakeIfStopped() err := w.Wake(ctx)
if err != nil { if err != nil {
gphttp.ServerError(rw, r, err) gphttp.ServerError(rw, r, err)
return false return false
} }
var ready bool
for { for {
w.resetIdleTimer() w.resetIdleTimer()
if w.cancelled(ctx) { if w.canceled(ctx) {
gphttp.ServerError(rw, r, context.Cause(ctx), http.StatusServiceUnavailable) w.redirectToStartEndpoint(rw, r)
return false return false
} }
w, ready, err = checkUpdateState(w.Key()) if !w.waitStarted(ctx) {
return false
}
ready, err := w.checkUpdateState()
if err != nil { if err != nil {
gphttp.ServerError(rw, r, err) gphttp.ServerError(rw, r, err)
return false return false

View file

@ -2,49 +2,54 @@ package idlewatcher
import ( import (
"context" "context"
"errors"
"net" "net"
"time" "time"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
// Setup implements types.Stream. var _ nettypes.Stream = (*Watcher)(nil)
func (w *Watcher) Addr() net.Addr {
return w.stream.Addr() // ListenAndServe implements nettypes.Stream.
func (w *Watcher) ListenAndServe(ctx context.Context, predial, onRead nettypes.HookFunc) {
w.stream.ListenAndServe(ctx, func(ctx context.Context) error { //nolint:contextcheck
return w.preDial(ctx, predial)
}, func(ctx context.Context) error {
return w.onRead(ctx, onRead)
})
} }
// Setup implements types.Stream. // Close implements nettypes.Stream.
func (w *Watcher) Setup() error {
return w.stream.Setup()
}
// Accept implements types.Stream.
func (w *Watcher) Accept() (conn gpnet.StreamConn, err error) {
conn, err = w.stream.Accept()
if err != nil {
return
}
if wakeErr := w.wakeFromStream(); wakeErr != nil {
w.l.Err(wakeErr).Msg("error waking container")
}
return
}
// Handle implements types.Stream.
func (w *Watcher) Handle(conn gpnet.StreamConn) error {
if err := w.wakeFromStream(); err != nil {
return err
}
return w.stream.Handle(conn)
}
// Close implements types.Stream.
func (w *Watcher) Close() error { func (w *Watcher) Close() error {
return w.stream.Close() return w.stream.Close()
} }
func (w *Watcher) wakeFromStream() error { // LocalAddr implements nettypes.Stream.
func (w *Watcher) LocalAddr() net.Addr {
return w.stream.LocalAddr()
}
func (w *Watcher) preDial(ctx context.Context, predial nettypes.HookFunc) error {
if predial != nil {
if err := predial(ctx); err != nil {
return err
}
}
return w.wakeFromStream(ctx)
}
func (w *Watcher) onRead(ctx context.Context, onRead nettypes.HookFunc) error {
w.resetIdleTimer()
if onRead != nil {
if err := onRead(ctx); err != nil {
return err
}
}
return nil
}
func (w *Watcher) wakeFromStream(ctx context.Context) error {
w.resetIdleTimer() w.resetIdleTimer()
// pass through if container is already ready // pass through if container is already ready
@ -53,27 +58,27 @@ func (w *Watcher) wakeFromStream() error {
} }
w.l.Debug().Msg("wake signal received") w.l.Debug().Msg("wake signal received")
err := w.wakeIfStopped() err := w.Wake(ctx)
if err != nil { if err != nil {
return err return err
} }
ctx, cancel := context.WithTimeoutCause(w.task.Context(), w.cfg.WakeTimeout, errors.New("wake timeout"))
defer cancel()
var ready bool
for { for {
if w.cancelled(ctx) { w.resetIdleTimer()
return context.Cause(ctx)
if w.canceled(ctx) {
return nil
} }
w, ready, err = checkUpdateState(w.Key()) if !w.waitStarted(ctx) {
return nil
}
ready, err := w.checkUpdateState()
if err != nil { if err != nil {
return err return err
} }
if ready { if ready {
w.resetIdleTimer()
w.l.Debug().Stringer("url", w.hc.URL()).Msg("container is ready, passing through") w.l.Debug().Stringer("url", w.hc.URL()).Msg("container is ready, passing through")
return nil return nil
} }

View file

@ -1,7 +1,6 @@
package idlewatcher package idlewatcher
import ( import (
"errors"
"time" "time"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
@ -80,43 +79,6 @@ func (w *Watcher) Detail() string {
return "napping" return "napping"
} }
func checkUpdateState(key string) (w *Watcher, ready bool, err error) {
watcherMapMu.RLock()
w, ok := watcherMap[key]
if !ok {
watcherMapMu.RUnlock()
return nil, false, errors.New("watcher not found")
}
watcherMapMu.RUnlock()
// already ready
if w.ready() {
return w, true, nil
}
if !w.running() {
return w, false, nil
}
// the new container info not yet updated
if w.hc.URL().Host == "" {
return w, false, nil
}
res, err := w.hc.CheckHealth()
if err != nil {
w.setError(err)
return w, false, err
}
if res.Healthy {
w.setReady()
return w, true, nil
}
w.setStarting()
return w, false, nil
}
// MarshalJSON implements health.HealthMonitor. // MarshalJSON implements health.HealthMonitor.
func (w *Watcher) MarshalJSON() ([]byte, error) { func (w *Watcher) MarshalJSON() ([]byte, error) {
url := w.hc.URL() url := w.hc.URL()
@ -135,3 +97,32 @@ func (w *Watcher) MarshalJSON() ([]byte, error) {
Detail: detail, Detail: detail,
}).MarshalJSON() }).MarshalJSON()
} }
func (w *Watcher) checkUpdateState() (ready bool, err error) {
// already ready
if w.ready() {
return true, nil
}
if !w.running() {
return false, nil
}
// the new container info not yet updated
if w.hc.URL().Host == "" {
return false, nil
}
res, err := w.hc.CheckHealth()
if err != nil {
w.setError(err)
return false, err
}
if res.Healthy {
w.setReady()
return true, nil
}
w.setStarting()
return false, nil
}

View file

@ -10,16 +10,26 @@ import (
) )
type ( type (
Config struct { ProviderConfig struct {
Proxmox *ProxmoxConfig `json:"proxmox,omitempty"` Proxmox *ProxmoxConfig `json:"proxmox,omitempty"`
Docker *DockerConfig `json:"docker,omitempty"` Docker *DockerConfig `json:"docker,omitempty"`
}
IdlewatcherConfig 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 StopMethod `json:"stop_method"`
StopSignal Signal `json:"stop_signal,omitempty"`
}
Config struct {
ProviderConfig
IdlewatcherConfig
IdleTimeout time.Duration `json:"idle_timeout" json_ext:"duration"` StartEndpoint string `json:"start_endpoint,omitempty"` // Optional path that must be hit to start container
WakeTimeout time.Duration `json:"wake_timeout" json_ext:"duration"` DependsOn []string `json:"depends_on,omitempty"`
StopTimeout time.Duration `json:"stop_timeout" json_ext:"duration"`
StopMethod StopMethod `json:"stop_method"`
StopSignal Signal `json:"stop_signal,omitempty"`
StartEndpoint string `json:"start_endpoint,omitempty"` // Optional path that must be hit to start container
} }
StopMethod string StopMethod string
Signal string Signal string
@ -55,11 +65,11 @@ func (c *Config) ContainerName() string {
if c.Docker != nil { if c.Docker != nil {
return c.Docker.ContainerName return c.Docker.ContainerName
} }
return "lxc " + strconv.Itoa(c.Proxmox.VMID) return "lxc-" + strconv.Itoa(c.Proxmox.VMID)
} }
func (c *Config) Validate() gperr.Error { func (c *Config) Validate() gperr.Error {
if c.IdleTimeout == 0 { // no idle timeout means no idle watcher if c.IdleTimeout == 0 { // zero idle timeout means no idle watcher
return nil return nil
} }
errs := gperr.NewBuilder("idlewatcher config validation error") errs := gperr.NewBuilder("idlewatcher config validation error")

View file

@ -35,7 +35,8 @@ func TestValidateStartEndpoint(t *testing.T) {
} }
for _, tc := range tests { for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cfg := Config{StartEndpoint: tc.input} cfg := new(Config)
cfg.StartEndpoint = tc.input
err := cfg.validateStartEndpoint() err := cfg.validateStartEndpoint()
if err == nil { if err == nil {
expect.Equal(t, cfg.StartEndpoint, tc.input) expect.Equal(t, cfg.StartEndpoint, tc.input)

View file

@ -3,13 +3,13 @@ package idlewatcher
import ( import (
"net/http" "net/http"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
) )
type Waker interface { type Waker interface {
health.HealthMonitor health.HealthMonitor
http.Handler http.Handler
net.Stream nettypes.Stream
Wake() error Wake() error
} }

View file

@ -3,6 +3,8 @@ package idlewatcher
import ( import (
"context" "context"
"errors" "errors"
"maps"
"strings"
"sync" "sync"
"time" "time"
@ -12,7 +14,7 @@ import (
"github.com/yusing/go-proxy/internal/idlewatcher/provider" "github.com/yusing/go-proxy/internal/idlewatcher/provider"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
@ -20,12 +22,15 @@ import (
"github.com/yusing/go-proxy/internal/watcher/events" "github.com/yusing/go-proxy/internal/watcher/events"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/singleflight"
) )
type ( type (
routeHelper struct { routeHelper struct {
route routes.Route
rp *reverseproxy.ReverseProxy rp *reverseproxy.ReverseProxy
stream net.Stream stream nettypes.Stream
hc health.HealthChecker hc health.HealthChecker
} }
@ -48,8 +53,15 @@ type (
state atomic.Value[*containerState] state atomic.Value[*containerState]
lastReset atomic.Value[time.Time] lastReset atomic.Value[time.Time]
ticker *time.Ticker idleTicker *time.Ticker
task *task.Task task *task.Task
dependsOn []*dependency
}
dependency struct {
*Watcher
waitHealthy bool
} }
StopCallback func() error StopCallback func() error
@ -60,6 +72,7 @@ const ContextKey = "idlewatcher.watcher"
var ( var (
watcherMap = make(map[string]*Watcher) watcherMap = make(map[string]*Watcher)
watcherMapMu sync.RWMutex watcherMapMu sync.RWMutex
singleFlight singleflight.Group
) )
const ( const (
@ -79,70 +92,193 @@ var (
const reqTimeout = 3 * time.Second const reqTimeout = 3 * time.Second
// prevents dependencies from being stopped automatically.
const neverTick = time.Duration(1<<63 - 1)
// TODO: fix stream type. // TODO: fix stream type.
func NewWatcher(parent task.Parent, r routes.Route) (*Watcher, error) { func NewWatcher(parent task.Parent, r routes.Route, cfg *idlewatcher.Config) (*Watcher, error) {
cfg := r.IdlewatcherConfig()
key := cfg.Key() key := cfg.Key()
watcherMapMu.RLock() watcherMapMu.RLock()
// if the watcher already exists, finish it // if the watcher already exists, finish it
w, exists := watcherMap[key] w, exists := watcherMap[key]
if exists {
if w.cfg == cfg {
// same address, likely two routes from the same container
return w, nil
}
w.task.Finish(causeReload)
}
watcherMapMu.RUnlock() watcherMapMu.RUnlock()
w = &Watcher{ if exists {
ticker: time.NewTicker(cfg.IdleTimeout), if len(cfg.DependsOn) > 0 {
cfg: cfg, w.cfg.DependsOn = cfg.DependsOn
routeHelper: routeHelper{ }
hc: monitor.NewMonitor(r), if cfg.IdleTimeout > 0 {
}, w.cfg.IdlewatcherConfig = cfg.IdlewatcherConfig
}
cfg = w.cfg
w.resetIdleTimer()
} else {
w = &Watcher{
idleTicker: time.NewTicker(cfg.IdleTimeout),
cfg: cfg,
routeHelper: routeHelper{
hc: monitor.NewMonitor(r),
},
dependsOn: make([]*dependency, 0, len(cfg.DependsOn)),
}
}
depErrors := gperr.NewBuilder()
for i, dep := range cfg.DependsOn {
depSegments := strings.Split(dep, ":")
dep = depSegments[0]
if dep == "" { // empty dependency (likely stopped container), skip; it will be removed by dedupDependencies()
continue
}
cfg.DependsOn[i] = dep
waitHealthy := false
if len(depSegments) > 1 { // likely from `com.docker.compose.depends_on` label
switch depSegments[1] {
case "service_started":
case "service_healthy":
waitHealthy = true
// case "service_completed_successfully":
default:
depErrors.Addf("dependency %q has unsupported condition %q", dep, depSegments[1])
continue
}
}
cont := r.ContainerInfo()
var depRoute routes.Route
var ok bool
// try to find the dependency in the same provider and the same docker compose project first
if cont != nil {
depRoute, ok = r.GetProvider().FindService(cont.DockerComposeProject(), dep)
}
if !ok {
depRoute, ok = routes.Get(dep)
if !ok {
depErrors.Addf("dependency %q not found", dep)
continue
}
}
if depRoute == r {
depErrors.Addf("dependency %q cannot have itself as a dependency (same route)", dep)
continue
}
// wait for the dependency to be started
<-depRoute.Started()
if waitHealthy && !depRoute.UseHealthCheck() {
depErrors.Addf("dependency %q has service_healthy condition but has healthcheck disabled", dep)
continue
}
depCfg := depRoute.IdlewatcherConfig()
if depCfg == nil {
depCfg = new(idlewatcher.Config)
depCfg.IdlewatcherConfig = cfg.IdlewatcherConfig
depCfg.IdleTimeout = neverTick // disable auto sleep for dependencies
} else if depCfg.IdleTimeout > 0 {
depErrors.Addf("dependency %q has positive idle timeout %s", dep, depCfg.IdleTimeout)
continue
}
if depCfg.Docker == nil && depCfg.Proxmox == nil {
depCont := depRoute.ContainerInfo()
if depCont != nil {
depCfg.Docker = &idlewatcher.DockerConfig{
DockerHost: depCont.DockerHost,
ContainerID: depCont.ContainerID,
ContainerName: depCont.ContainerName,
}
depCfg.DependsOn = depCont.Dependencies()
} else {
depErrors.Addf("dependency %q has no idlewatcher config but is not a docker container", dep)
continue
}
}
if depCfg.Key() == cfg.Key() {
depErrors.Addf("dependency %q cannot have itself as a dependency (same container)", dep)
continue
}
depCfg.IdleTimeout = neverTick // disable auto sleep for dependencies
depWatcher, err := NewWatcher(parent, depRoute, depCfg)
if err != nil {
depErrors.Add(err)
continue
}
w.dependsOn = append(w.dependsOn, &dependency{
Watcher: depWatcher,
waitHealthy: waitHealthy,
})
}
if w.provider != nil { // it's a reload, close the old provider
w.provider.Close()
}
if depErrors.HasError() {
return nil, depErrors.Error()
}
if !exists {
watcherMapMu.Lock()
defer watcherMapMu.Unlock()
} }
var p idlewatcher.Provider var p idlewatcher.Provider
var providerType string
var err error var err error
var kind string
switch { switch {
case cfg.Docker != nil: case cfg.Docker != nil:
p, err = provider.NewDockerProvider(cfg.Docker.DockerHost, cfg.Docker.ContainerID) p, err = provider.NewDockerProvider(cfg.Docker.DockerHost, cfg.Docker.ContainerID)
providerType = "docker" kind = "docker"
default: default:
p, err = provider.NewProxmoxProvider(cfg.Proxmox.Node, cfg.Proxmox.VMID) p, err = provider.NewProxmoxProvider(cfg.Proxmox.Node, cfg.Proxmox.VMID)
providerType = "proxmox" kind = "proxmox"
}
w.l = log.With().
Str("kind", kind).
Str("container", cfg.ContainerName()).
Logger()
if cfg.IdleTimeout != neverTick {
w.l = w.l.With().Stringer("idle_timeout", cfg.IdleTimeout).Logger()
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
w.provider = p w.provider = p
w.l = log.With().
Str("provider", providerType).
Str("container", cfg.ContainerName()).
Logger()
switch r := r.(type) { switch r := r.(type) {
case routes.ReverseProxyRoute: case routes.ReverseProxyRoute:
w.rp = r.ReverseProxy() w.rp = r.ReverseProxy()
case routes.StreamRoute: case routes.StreamRoute:
w.stream = r w.stream = r.Stream()
default: default:
return nil, gperr.Errorf("unexpected route type: %T", r) w.provider.Close()
return nil, w.newWatcherError(gperr.Errorf("unexpected route type: %T", r))
} }
w.route = r
ctx, cancel := context.WithTimeout(parent.Context(), reqTimeout) ctx, cancel := context.WithTimeout(parent.Context(), reqTimeout)
defer cancel() defer cancel()
status, err := w.provider.ContainerStatus(ctx) status, err := w.provider.ContainerStatus(ctx)
if err != nil { if err != nil {
w.provider.Close() w.provider.Close()
return nil, gperr.Wrap(err, "failed to get container status") return nil, w.newWatcherError(err)
} }
w.state.Store(&containerState{status: status})
switch p := w.provider.(type) { // when more providers are added, we need to add a new case here.
switch p := w.provider.(type) { //nolint:gocritic
case *provider.ProxmoxProvider: case *provider.ProxmoxProvider:
shutdownTimeout := max(time.Second, cfg.StopTimeout-idleWakerCheckTimeout) shutdownTimeout := max(time.Second, cfg.StopTimeout-idleWakerCheckTimeout)
err = p.LXCSetShutdownTimeout(ctx, cfg.Proxmox.VMID, shutdownTimeout) err = p.LXCSetShutdownTimeout(ctx, cfg.Proxmox.VMID, shutdownTimeout)
@ -151,30 +287,40 @@ func NewWatcher(parent task.Parent, r routes.Route) (*Watcher, error) {
} }
} }
w.state.Store(&containerState{status: status}) if !exists {
w.task = parent.Subtask("idlewatcher."+r.Name(), true)
watcherMap[key] = w
w.task = parent.Subtask("idlewatcher."+r.Name(), true) go func() {
cause := w.watchUntilDestroy()
if errors.Is(cause, causeContainerDestroy) || errors.Is(cause, task.ErrProgramExiting) {
watcherMapMu.Lock()
delete(watcherMap, key)
watcherMapMu.Unlock()
w.l.Info().Msg("idlewatcher stopped")
} else if !errors.Is(cause, causeReload) {
gperr.LogError("idlewatcher stopped unexpectedly", cause, &w.l)
}
watcherMapMu.Lock() w.idleTicker.Stop()
defer watcherMapMu.Unlock() w.provider.Close()
watcherMap[key] = w w.task.Finish(cause)
go func() { }()
cause := w.watchUntilDestroy() }
if errors.Is(cause, causeContainerDestroy) || errors.Is(cause, task.ErrProgramExiting) {
watcherMapMu.Lock()
defer watcherMapMu.Unlock()
delete(watcherMap, key)
w.l.Info().Msg("idlewatcher stopped")
} else if !errors.Is(cause, causeReload) {
gperr.LogError("idlewatcher stopped unexpectedly", cause, &w.l)
}
w.ticker.Stop() hcCfg := w.hc.Config()
w.provider.Close() hcCfg.BaseContext = func() context.Context {
w.task.Finish(cause) return w.task.Context()
}() }
hcCfg.Timeout = cfg.WakeTimeout
w.dedupDependencies()
r.SetHealthMonitor(w)
w.l = w.l.With().Strs("deps", cfg.DependsOn).Logger()
if exists { if exists {
w.l.Info().Msg("idlewatcher reloaded") w.l.Debug().Msg("idlewatcher reloaded")
} else { } else {
w.l.Info().Msg("idlewatcher started") w.l.Info().Msg("idlewatcher started")
} }
@ -185,18 +331,75 @@ func (w *Watcher) Key() string {
return w.cfg.Key() return w.cfg.Key()
} }
func (w *Watcher) Wake() error { // Wake wakes the container.
return w.wakeIfStopped() //
// It will cancel as soon as the either of the passed in context or the watcher is done.
//
// It uses singleflight to prevent multiple wake calls at the same time.
//
// It will wake the dependencies first, and then wake itself.
// If the container is already running, it will do nothing.
// If the container is not running, it will start it.
// If the container is paused, it will unpause it.
// If the container is stopped, it will do nothing.
func (w *Watcher) Wake(ctx context.Context) error {
// wake dependencies first.
if err := w.wakeDependencies(ctx); err != nil {
return w.newWatcherError(err)
}
// wake itself.
// use container name instead of Key() here as the container id will change on restart (docker).
_, err, _ := singleFlight.Do(w.cfg.ContainerName(), func() (any, error) {
return nil, w.wakeIfStopped(ctx)
})
if err != nil {
return w.newWatcherError(err)
}
return nil
} }
func (w *Watcher) wakeIfStopped() error { func (w *Watcher) wakeDependencies(ctx context.Context) error {
if len(w.dependsOn) == 0 {
return nil
}
errs := errgroup.Group{}
for _, dep := range w.dependsOn {
errs.Go(func() error {
if err := dep.Wake(ctx); err != nil {
return err
}
if dep.waitHealthy {
for {
select {
case <-ctx.Done():
return w.newDepError("wait_healthy", dep, context.Cause(ctx))
default:
if h, err := dep.hc.CheckHealth(); err != nil {
return err
} else if h.Healthy {
return nil
}
time.Sleep(idleWakerCheckInterval)
}
}
}
return nil
})
}
return errs.Wait()
}
func (w *Watcher) wakeIfStopped(ctx context.Context) error {
state := w.state.Load() state := w.state.Load()
if state.status == idlewatcher.ContainerStatusRunning { if state.status == idlewatcher.ContainerStatusRunning {
w.l.Debug().Msg("container is already running") w.l.Debug().Msg("container is already running")
return nil return nil
} }
ctx, cancel := context.WithTimeout(w.task.Context(), w.cfg.WakeTimeout) ctx, cancel := context.WithTimeout(ctx, w.cfg.WakeTimeout)
defer cancel() defer cancel()
switch state.status { switch state.status {
case idlewatcher.ContainerStatusStopped: case idlewatcher.ContainerStatusStopped:
@ -210,34 +413,66 @@ func (w *Watcher) wakeIfStopped() error {
} }
} }
func (w *Watcher) stopDependencies() error {
if len(w.dependsOn) == 0 {
return nil
}
errs := errgroup.Group{}
for _, dep := range w.dependsOn {
errs.Go(dep.stopByMethod)
}
return errs.Wait()
}
func (w *Watcher) stopByMethod() error { func (w *Watcher) stopByMethod() error {
// no need singleflight here because it will only be called once every tick.
// if the container is not running, skip and stop dependencies.
if !w.running() { if !w.running() {
if err := w.stopDependencies(); err != nil {
return w.newWatcherError(err)
}
return nil return nil
} }
cfg := w.cfg cfg := w.cfg
ctx, cancel := context.WithTimeout(w.task.Context(), cfg.StopTimeout) ctx, cancel := context.WithTimeout(context.Background(), cfg.StopTimeout)
defer cancel() defer cancel()
// stop itself first.
var err error
switch cfg.StopMethod { switch cfg.StopMethod {
case idlewatcher.StopMethodPause: case idlewatcher.StopMethodPause:
return w.provider.ContainerPause(ctx) err = w.provider.ContainerPause(ctx)
case idlewatcher.StopMethodStop: case idlewatcher.StopMethodStop:
return w.provider.ContainerStop(ctx, cfg.StopSignal, int(cfg.StopTimeout.Seconds())) err = w.provider.ContainerStop(ctx, cfg.StopSignal, int(cfg.StopTimeout.Seconds()))
case idlewatcher.StopMethodKill: case idlewatcher.StopMethodKill:
return w.provider.ContainerKill(ctx, cfg.StopSignal) err = w.provider.ContainerKill(ctx, cfg.StopSignal)
default: default:
return gperr.Errorf("unexpected stop method: %q", cfg.StopMethod) err = w.newWatcherError(gperr.Errorf("unexpected stop method: %q", cfg.StopMethod))
} }
if err != nil {
return w.newWatcherError(err)
}
w.l.Info().Msg("container stopped")
// then stop dependencies.
if err := w.stopDependencies(); err != nil {
return w.newWatcherError(err)
}
return nil
} }
func (w *Watcher) resetIdleTimer() { func (w *Watcher) resetIdleTimer() {
w.ticker.Reset(w.cfg.IdleTimeout) w.idleTicker.Reset(w.cfg.IdleTimeout)
w.lastReset.Store(time.Now()) w.lastReset.Store(time.Now())
} }
func (w *Watcher) expires() time.Time { func (w *Watcher) expires() time.Time {
if !w.running() { if !w.running() || w.cfg.IdleTimeout <= 0 {
return time.Time{} return time.Time{}
} }
return w.lastReset.Load().Add(w.cfg.IdleTimeout) return w.lastReset.Load().Add(w.cfg.IdleTimeout)
@ -278,15 +513,15 @@ func (w *Watcher) watchUntilDestroy() (returnCause error) {
w.l.Info().Msg("awaken") w.l.Info().Msg("awaken")
case e.Action.IsContainerStop(): // stop / kill / die case e.Action.IsContainerStop(): // stop / kill / die
w.setNapping(idlewatcher.ContainerStatusStopped) w.setNapping(idlewatcher.ContainerStatusStopped)
w.ticker.Stop() w.idleTicker.Stop()
case e.Action.IsContainerPause(): // pause case e.Action.IsContainerPause(): // pause
w.setNapping(idlewatcher.ContainerStatusPaused) w.setNapping(idlewatcher.ContainerStatusPaused)
w.ticker.Stop() w.idleTicker.Stop()
default: default:
w.l.Error().Stringer("action", e.Action).Msg("unexpected container action") w.l.Debug().Stringer("action", e.Action).Msg("unexpected container action")
} }
case <-w.ticker.C: case <-w.idleTicker.C:
w.ticker.Stop() w.idleTicker.Stop()
if w.running() { if w.running() {
err := w.stopByMethod() err := w.stopByMethod()
switch { switch {
@ -298,16 +533,37 @@ func (w *Watcher) watchUntilDestroy() (returnCause error) {
} }
w.l.Err(err).Msgf("container stop with method %q failed", w.cfg.StopMethod) w.l.Err(err).Msgf("container stop with method %q failed", w.cfg.StopMethod)
default: default:
w.l.Info().Str("reason", "idle timeout").Msg("container stopped") w.l.Info().Msg("idle timeout")
} }
} }
} }
} }
} }
func fmtErr(err error) string { func (w *Watcher) dedupDependencies() {
if err == nil { // remove from dependencies if the dependency is also a dependency of another dependency, or have duplicates.
return "" deps := w.dependencies()
for _, dep := range w.dependsOn {
depdeps := dep.dependencies()
for depdep := range depdeps {
delete(deps, depdep)
}
} }
return err.Error() newDepOn := make([]string, 0, len(deps))
newDeps := make([]*dependency, 0, len(deps))
for _, dep := range deps {
newDepOn = append(newDepOn, dep.cfg.ContainerName())
newDeps = append(newDeps, dep)
}
w.cfg.DependsOn = newDepOn
w.dependsOn = newDeps
}
func (w *Watcher) dependencies() map[string]*dependency {
deps := make(map[string]*dependency)
for _, dep := range w.dependsOn {
deps[dep.Key()] = dep
maps.Copy(deps, dep.dependencies())
}
return deps
} }

View file

@ -6,7 +6,7 @@ import (
"strings" "strings"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -25,7 +25,7 @@ type (
} }
Host string Host string
CIDR struct { CIDR struct {
gpnet.CIDR nettypes.CIDR
} }
) )

View file

@ -6,7 +6,7 @@ import (
"testing" "testing"
. "github.com/yusing/go-proxy/internal/logging/accesslog" . "github.com/yusing/go-proxy/internal/logging/accesslog"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
expect "github.com/yusing/go-proxy/internal/utils/testing" expect "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -157,7 +157,7 @@ func TestHeaderFilter(t *testing.T) {
} }
func TestCIDRFilter(t *testing.T) { func TestCIDRFilter(t *testing.T) {
cidr := []*CIDR{{gpnet.CIDR{ cidr := []*CIDR{{nettypes.CIDR{
IP: net.ParseIP("192.168.10.0"), IP: net.ParseIP("192.168.10.0"),
Mask: net.CIDRMask(24, 32), Mask: net.CIDRMask(24, 32),
}}} }}}

View file

@ -73,7 +73,6 @@ func (m *memLogger) Write(p []byte) (n int, err error) {
func (m *memLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (m *memLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
conn, err := gpwebsocket.Initiate(w, r) conn, err := gpwebsocket.Initiate(w, r)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View file

@ -34,11 +34,16 @@ type (
City = maxmind.City City = maxmind.City
) )
var ( const (
updateInterval = 24 * time.Hour updateInterval = 24 * time.Hour
httpClient = &http.Client{ updateTimeout = 10 * time.Second
Timeout: 10 * time.Second, )
}
var httpClient = &http.Client{
Timeout: updateTimeout,
}
var (
ErrResponseNotOK = gperr.New("response not OK") ErrResponseNotOK = gperr.New("response not OK")
ErrDownloadFailure = gperr.New("download failure") ErrDownloadFailure = gperr.New("download failure")
) )

View file

@ -166,12 +166,12 @@ func (s *SystemInfo) collectDisksInfo(ctx context.Context, lastResult *SystemInf
} }
s.DisksIO = ioCounters s.DisksIO = ioCounters
if lastResult != nil { if lastResult != nil {
interval := time.Now().Unix() - lastResult.Timestamp interval := since(lastResult.Timestamp)
for name, disk := range s.DisksIO { for name, disk := range s.DisksIO {
if lastUsage, ok := lastResult.DisksIO[name]; ok { if lastUsage, ok := lastResult.DisksIO[name]; ok {
disk.ReadSpeed = float64(disk.ReadBytes-lastUsage.ReadBytes) / float64(interval) disk.ReadSpeed = float64(disk.ReadBytes-lastUsage.ReadBytes) / float64(interval)
disk.WriteSpeed = float64(disk.WriteBytes-lastUsage.WriteBytes) / float64(interval) disk.WriteSpeed = float64(disk.WriteBytes-lastUsage.WriteBytes) / float64(interval)
disk.Iops = (disk.ReadCount + disk.WriteCount - lastUsage.ReadCount - lastUsage.WriteCount) / uint64(interval) disk.Iops = diff(disk.ReadCount+disk.WriteCount, lastUsage.ReadCount+lastUsage.WriteCount) / uint64(interval) //nolint:gosec
} }
} }
} }
@ -207,7 +207,7 @@ func (s *SystemInfo) collectNetworkInfo(ctx context.Context, lastResult *SystemI
} }
s.Network = networkIO[0] s.Network = networkIO[0]
if lastResult != nil { if lastResult != nil {
interval := float64(time.Now().Unix() - lastResult.Timestamp) interval := float64(since(lastResult.Timestamp))
s.Network.UploadSpeed = float64(networkIO[0].BytesSent-lastResult.Network.BytesSent) / interval s.Network.UploadSpeed = float64(networkIO[0].BytesSent-lastResult.Network.BytesSent) / interval
s.Network.DownloadSpeed = float64(networkIO[0].BytesRecv-lastResult.Network.BytesRecv) / interval s.Network.DownloadSpeed = float64(networkIO[0].BytesRecv-lastResult.Network.BytesRecv) / interval
} }
@ -342,3 +342,21 @@ func aggregate(entries []*SystemInfo, query url.Values) (total int, result Aggre
} }
return len(aggregated), aggregated return len(aggregated), aggregated
} }
func diff(x, y uint64) uint64 {
if x > y {
return x - y
}
return y - x
}
func since(last int64) int64 {
now := time.Now().Unix()
if last > now { // should not happen but just in case
return 1
}
if last == now { // two consecutive polls occur within the same second
return 1
}
return now - last
}

View file

@ -3,13 +3,13 @@ package gpwebsocket
import ( import (
"net" "net"
"net/http" "net/http"
"slices"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/agent/pkg/agent"
) )
func warnNoMatchDomains() { func warnNoMatchDomains() {
@ -30,12 +30,17 @@ func SetWebsocketAllowedDomains(h http.Header, domains []string) {
h[HeaderXGoDoxyWebsocketAllowedDomains] = domains h[HeaderXGoDoxyWebsocketAllowedDomains] = domains
} }
var localAddresses = []string{"127.0.0.1", "10.0.*.*", "172.16.*.*", "192.168.*.*"}
const writeTimeout = time.Second * 10 const writeTimeout = time.Second * 10
// Initiate upgrades the HTTP connection to a WebSocket connection.
// It returns a WebSocket connection and an error if the upgrade fails.
// It logs and responds with an error if the upgrade fails.
//
// No further http.Error should be called after this function.
func Initiate(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) { func Initiate(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
upgrader := websocket.Upgrader{} upgrader := websocket.Upgrader{
Error: errHandler,
}
allowedDomains := WebsocketAllowedDomains(r.Header) allowedDomains := WebsocketAllowedDomains(r.Header)
if len(allowedDomains) == 0 { if len(allowedDomains) == 0 {
@ -49,9 +54,13 @@ func Initiate(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
if err != nil { if err != nil {
host = r.Host host = r.Host
} }
if slices.Contains(localAddresses, host) { if host == "localhost" || host == agent.AgentHost {
return true return true
} }
ip := net.ParseIP(host)
if ip != nil {
return ip.IsLoopback() || ip.IsPrivate()
}
for _, domain := range allowedDomains { for _, domain := range allowedDomains {
if domain[0] == '.' { if domain[0] == '.' {
if host == domain[1:] || strings.HasSuffix(host, domain) { if host == domain[1:] || strings.HasSuffix(host, domain) {
@ -70,7 +79,6 @@ func Initiate(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
func Periodic(w http.ResponseWriter, r *http.Request, interval time.Duration, do func(conn *websocket.Conn) error) { func Periodic(w http.ResponseWriter, r *http.Request, interval time.Duration, do func(conn *websocket.Conn) error) {
conn, err := Initiate(w, r) conn, err := Initiate(w, r)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
defer conn.Close() defer conn.Close()
@ -102,3 +110,15 @@ func WriteText(conn *websocket.Conn, msg string) error {
_ = conn.SetWriteDeadline(time.Now().Add(writeTimeout)) _ = conn.SetWriteDeadline(time.Now().Add(writeTimeout))
return conn.WriteMessage(websocket.TextMessage, []byte(msg)) return conn.WriteMessage(websocket.TextMessage, []byte(msg))
} }
func errHandler(w http.ResponseWriter, r *http.Request, status int, reason error) {
log.Error().
Str("remote", r.RemoteAddr).
Str("host", r.Host).
Str("url", r.URL.String()).
Int("status", status).
AnErr("reason", reason).
Msg("websocket error")
w.Header().Set("Sec-Websocket-Version", "13")
http.Error(w, http.StatusText(status), status)
}

View file

@ -4,7 +4,7 @@ import (
"net/http" "net/http"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
) )
@ -14,7 +14,7 @@ type (
_ U.NoCopy _ U.NoCopy
name string name string
url *net.URL url *nettypes.URL
weight Weight weight Weight
http.Handler `json:"-"` http.Handler `json:"-"`
@ -26,14 +26,14 @@ type (
health.HealthMonitor health.HealthMonitor
Name() string Name() string
Key() string Key() string
URL() *net.URL URL() *nettypes.URL
Weight() Weight Weight() Weight
SetWeight(weight Weight) SetWeight(weight Weight)
TryWake() error TryWake() error
} }
) )
func NewServer(name string, url *net.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) Server { func NewServer(name string, url *nettypes.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) Server {
srv := &server{ srv := &server{
name: name, name: name,
url: url, url: url,
@ -47,7 +47,7 @@ func NewServer(name string, url *net.URL, weight Weight, handler http.Handler, h
func TestNewServer[T ~int | ~float32 | ~float64](weight T) Server { func TestNewServer[T ~int | ~float32 | ~float64](weight T) Server {
srv := &server{ srv := &server{
weight: Weight(weight), weight: Weight(weight),
url: net.MustParseURL("http://localhost"), url: nettypes.MustParseURL("http://localhost"),
} }
return srv return srv
} }
@ -56,7 +56,7 @@ func (srv *server) Name() string {
return srv.name return srv.name
} }
func (srv *server) URL() *net.URL { func (srv *server) URL() *nettypes.URL {
return srv.url return srv.url
} }

View file

@ -10,7 +10,7 @@ import (
"github.com/yusing/go-proxy/internal/entrypoint" "github.com/yusing/go-proxy/internal/entrypoint"
. "github.com/yusing/go-proxy/internal/net/gphttp/middleware" . "github.com/yusing/go-proxy/internal/net/gphttp/middleware"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route" "github.com/yusing/go-proxy/internal/route"
routeTypes "github.com/yusing/go-proxy/internal/route/types" routeTypes "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
@ -99,7 +99,7 @@ func (f fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
} }
func TestReverseProxyBypass(t *testing.T) { func TestReverseProxyBypass(t *testing.T) {
rp := reverseproxy.NewReverseProxy("test", types.MustParseURL("http://example.com"), fakeRoundTripper{}) rp := reverseproxy.NewReverseProxy("test", nettypes.MustParseURL("http://example.com"), fakeRoundTripper{})
err := PatchReverseProxy(rp, map[string]OptionsRaw{ err := PatchReverseProxy(rp, map[string]OptionsRaw{
"response": { "response": {
"bypass": "path /test/* | path /api", "bypass": "path /test/* | path /api",

View file

@ -6,7 +6,7 @@ import (
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/serialization" "github.com/yusing/go-proxy/internal/serialization"
F "github.com/yusing/go-proxy/internal/utils/functional" F "github.com/yusing/go-proxy/internal/utils/functional"
) )
@ -14,12 +14,11 @@ import (
type ( type (
cidrWhitelist struct { cidrWhitelist struct {
CIDRWhitelistOpts CIDRWhitelistOpts
Tracer
cachedAddr F.Map[string, bool] // cache for trusted IPs cachedAddr F.Map[string, bool] // cache for trusted IPs
} }
CIDRWhitelistOpts struct { CIDRWhitelistOpts struct {
Allow []*types.CIDR `validate:"min=1"` Allow []*nettypes.CIDR `validate:"min=1"`
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"` StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
Message string Message string
} }
) )
@ -27,7 +26,7 @@ type (
var ( var (
CIDRWhiteList = NewMiddleware[cidrWhitelist]() CIDRWhiteList = NewMiddleware[cidrWhitelist]()
cidrWhitelistDefaults = CIDRWhitelistOpts{ cidrWhitelistDefaults = CIDRWhitelistOpts{
Allow: []*types.CIDR{}, Allow: []*nettypes.CIDR{},
StatusCode: http.StatusForbidden, StatusCode: http.StatusForbidden,
Message: "IP not allowed", Message: "IP not allowed",
} }
@ -64,13 +63,11 @@ func (wl *cidrWhitelist) checkIP(w http.ResponseWriter, r *http.Request) bool {
if cidr.Contains(ip) { if cidr.Contains(ip) {
wl.cachedAddr.Store(r.RemoteAddr, true) wl.cachedAddr.Store(r.RemoteAddr, true)
allow = true allow = true
wl.AddTracef("client %s is allowed", ipStr).With("allowed CIDR", cidr)
break break
} }
} }
if !allow { if !allow {
wl.cachedAddr.Store(r.RemoteAddr, false) wl.cachedAddr.Store(r.RemoteAddr, false)
wl.AddTracef("client %s is forbidden", ipStr).With("allowed CIDRs", wl.Allow)
} }
} }
if !allow { if !allow {

View file

@ -12,7 +12,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/atomic" "github.com/yusing/go-proxy/internal/utils/atomic"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -33,7 +33,7 @@ var (
cfCIDRsMu sync.Mutex cfCIDRsMu sync.Mutex
// RFC 1918. // RFC 1918.
localCIDRs = []*types.CIDR{ localCIDRs = []*nettypes.CIDR{
{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 255)}, // 127.0.0.1/32 {IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 255)}, // 127.0.0.1/32
{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}, // 10.0.0.0/8 {IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}, // 10.0.0.0/8
{IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)}, // 172.16.0.0/12 {IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)}, // 172.16.0.0/12
@ -45,7 +45,7 @@ var CloudflareRealIP = NewMiddleware[cloudflareRealIP]()
// setup implements MiddlewareWithSetup. // setup implements MiddlewareWithSetup.
func (cri *cloudflareRealIP) setup() { func (cri *cloudflareRealIP) setup() {
cri.realIP.RealIPOpts = RealIPOpts{ cri.realIP = realIP{
Header: "CF-Connecting-IP", Header: "CF-Connecting-IP",
Recursive: true, Recursive: true,
} }
@ -60,15 +60,7 @@ func (cri *cloudflareRealIP) before(w http.ResponseWriter, r *http.Request) bool
return cri.realIP.before(w, r) return cri.realIP.before(w, r)
} }
func (cri *cloudflareRealIP) enableTrace() { func tryFetchCFCIDR() (cfCIDRs []*nettypes.CIDR) {
cri.realIP.enableTrace()
}
func (cri *cloudflareRealIP) getTracer() *Tracer {
return cri.realIP.getTracer()
}
func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval { if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
return return
} }
@ -83,7 +75,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
if common.IsTest { if common.IsTest {
cfCIDRs = localCIDRs cfCIDRs = localCIDRs
} else { } else {
cfCIDRs = make([]*types.CIDR, 0, 30) cfCIDRs = make([]*nettypes.CIDR, 0, 30)
err := errors.Join( err := errors.Join(
fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs), fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs),
fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs), fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs),
@ -103,7 +95,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
return return
} }
func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error { func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*nettypes.CIDR) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -132,7 +124,7 @@ func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error {
return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line) return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line)
} }
*cfCIDRs = append(*cfCIDRs, (*types.CIDR)(cidr)) *cfCIDRs = append(*cfCIDRs, (*nettypes.CIDR)(cidr))
} }
*cfCIDRs = append(*cfCIDRs, localCIDRs...) *cfCIDRs = append(*cfCIDRs, localCIDRs...)
return nil return nil

View file

@ -8,7 +8,6 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
@ -52,10 +51,6 @@ type (
MiddlewareFinalizerWithError interface { MiddlewareFinalizerWithError interface {
finalize() error finalize() error
} }
MiddlewareWithTracer interface {
enableTrace()
getTracer() *Tracer
}
) )
const DefaultPriority = 10 const DefaultPriority = 10
@ -84,26 +79,6 @@ func NewMiddleware[ImplType any]() *Middleware {
} }
} }
func (m *Middleware) enableTrace() {
if tracer, ok := m.impl.(MiddlewareWithTracer); ok {
tracer.enableTrace()
log.Trace().Msgf("middleware %s enabled trace", m.name)
}
}
func (m *Middleware) getTracer() *Tracer {
if tracer, ok := m.impl.(MiddlewareWithTracer); ok {
return tracer.getTracer()
}
return nil
}
func (m *Middleware) setParent(parent *Middleware) {
if tracer := m.getTracer(); tracer != nil {
tracer.SetParent(parent.getTracer())
}
}
func (m *Middleware) setup() { func (m *Middleware) setup() {
if setup, ok := m.impl.(MiddlewareWithSetup); ok { if setup, ok := m.impl.(MiddlewareWithSetup); ok {
setup.setup() setup.setup()

View file

@ -3,7 +3,6 @@ package middleware
import ( import (
"net/http" "net/http"
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
) )
@ -24,14 +23,6 @@ func NewMiddlewareChain(name string, chain []*Middleware) *Middleware {
if mr, ok := comp.impl.(ResponseModifier); ok { if mr, ok := comp.impl.(ResponseModifier); ok {
chainMid.modResps = append(chainMid.modResps, mr) chainMid.modResps = append(chainMid.modResps, mr)
} }
comp.setParent(m)
}
if common.IsTrace {
for _, child := range chain {
child.enableTrace()
}
m.enableTrace()
} }
return m return m
} }

View file

@ -9,7 +9,6 @@ import (
type ( type (
modifyRequest struct { modifyRequest struct {
ModifyRequestOpts ModifyRequestOpts
Tracer
} }
// order: add_prefix -> set_headers -> add_headers -> hide_headers // order: add_prefix -> set_headers -> add_headers -> hide_headers
ModifyRequestOpts struct { ModifyRequestOpts struct {
@ -31,11 +30,14 @@ func (mr *ModifyRequestOpts) finalize() {
// before implements RequestModifier. // before implements RequestModifier.
func (mr *modifyRequest) before(w http.ResponseWriter, r *http.Request) (proceed bool) { func (mr *modifyRequest) before(w http.ResponseWriter, r *http.Request) (proceed bool) {
mr.AddTraceRequest("before modify request", r) if len(mr.AddPrefix) != 0 {
mr.addPrefix(r, r.URL.Path)
mr.addPrefix(r, nil, r.URL.Path) }
mr.modifyHeaders(r, nil, r.Header) if !mr.needVarSubstitution {
mr.AddTraceRequest("after modify request", r) mr.modifyHeaders(r, r.Header)
} else {
mr.modifyHeadersWithVarSubstitution(r, nil, r.Header)
}
return true return true
} }
@ -50,42 +52,40 @@ func (mr *ModifyRequestOpts) checkVarSubstitution() {
} }
} }
func (mr *ModifyRequestOpts) modifyHeaders(req *http.Request, resp *http.Response, headers http.Header) { func (mr *ModifyRequestOpts) modifyHeaders(req *http.Request, headers http.Header) {
if !mr.needVarSubstitution { for k, v := range mr.SetHeaders {
for k, v := range mr.SetHeaders { if req != nil && strings.EqualFold(k, "host") {
if req != nil && strings.EqualFold(k, "host") { defer func() {
defer func() { req.Host = v
req.Host = v }()
}()
}
headers[k] = []string{v}
}
for k, v := range mr.AddHeaders {
headers[k] = append(headers[k], v)
}
} else {
for k, v := range mr.SetHeaders {
if req != nil && strings.EqualFold(k, "host") {
defer func() {
req.Host = varReplace(req, resp, v)
}()
}
headers[k] = []string{varReplace(req, resp, v)}
}
for k, v := range mr.AddHeaders {
headers[k] = append(headers[k], varReplace(req, resp, v))
} }
headers[k] = []string{v}
}
for k, v := range mr.AddHeaders {
headers[k] = append(headers[k], v)
} }
for _, k := range mr.HideHeaders { for _, k := range mr.HideHeaders {
delete(headers, k) delete(headers, k)
} }
} }
func (mr *modifyRequest) addPrefix(r *http.Request, _ *http.Response, path string) { func (mr *ModifyRequestOpts) modifyHeadersWithVarSubstitution(req *http.Request, resp *http.Response, headers http.Header) {
if len(mr.AddPrefix) == 0 { for k, v := range mr.SetHeaders {
return if req != nil && strings.EqualFold(k, "host") {
defer func() {
req.Host = varReplace(req, resp, v)
}()
}
headers[k] = []string{varReplace(req, resp, v)}
} }
for k, v := range mr.AddHeaders {
headers[k] = append(headers[k], varReplace(req, resp, v))
}
for _, k := range mr.HideHeaders {
delete(headers, k)
}
}
func (mr *modifyRequest) addPrefix(r *http.Request, path string) {
r.URL.Path = filepath.Join(mr.AddPrefix, path) r.URL.Path = filepath.Join(mr.AddPrefix, path)
} }

View file

@ -7,7 +7,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -51,8 +51,8 @@ func TestModifyRequest(t *testing.T) {
}) })
t.Run("request_headers", func(t *testing.T) { t.Run("request_headers", func(t *testing.T) {
reqURL := types.MustParseURL("https://my.app/?arg_1=b") reqURL := nettypes.MustParseURL("https://my.app/?arg_1=b")
upstreamURL := types.MustParseURL("http://test.example.com") upstreamURL := nettypes.MustParseURL("http://test.example.com")
result, err := newMiddlewareTest(ModifyRequest, &testArgs{ result, err := newMiddlewareTest(ModifyRequest, &testArgs{
middlewareOpt: opts, middlewareOpt: opts,
reqURL: reqURL, reqURL: reqURL,
@ -128,8 +128,8 @@ func TestModifyRequest(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
reqURL := types.MustParseURL("https://my.app" + tt.path) reqURL := nettypes.MustParseURL("https://my.app" + tt.path)
upstreamURL := types.MustParseURL(tt.upstreamURL) upstreamURL := nettypes.MustParseURL(tt.upstreamURL)
opts["add_prefix"] = tt.addPrefix opts["add_prefix"] = tt.addPrefix
result, err := newMiddlewareTest(ModifyRequest, &testArgs{ result, err := newMiddlewareTest(ModifyRequest, &testArgs{

View file

@ -6,15 +6,16 @@ import (
type modifyResponse struct { type modifyResponse struct {
ModifyRequestOpts ModifyRequestOpts
Tracer
} }
var ModifyResponse = NewMiddleware[modifyResponse]() var ModifyResponse = NewMiddleware[modifyResponse]()
// modifyResponse implements ResponseModifier. // modifyResponse implements ResponseModifier.
func (mr *modifyResponse) modifyResponse(resp *http.Response) error { func (mr *modifyResponse) modifyResponse(resp *http.Response) error {
mr.AddTraceResponse("before modify response", resp) if !mr.needVarSubstitution {
mr.modifyHeaders(resp.Request, resp, resp.Header) mr.modifyHeaders(resp.Request, resp.Header)
mr.AddTraceResponse("after modify response", resp) } else {
mr.modifyHeadersWithVarSubstitution(resp.Request, resp, resp.Header)
}
return nil return nil
} }

View file

@ -7,7 +7,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -54,8 +54,8 @@ func TestModifyResponse(t *testing.T) {
}) })
t.Run("response_headers", func(t *testing.T) { t.Run("response_headers", func(t *testing.T) {
reqURL := types.MustParseURL("https://my.app/?arg_1=b") reqURL := nettypes.MustParseURL("https://my.app/?arg_1=b")
upstreamURL := types.MustParseURL("http://test.example.com") upstreamURL := nettypes.MustParseURL("http://test.example.com")
result, err := newMiddlewareTest(ModifyResponse, &testArgs{ result, err := newMiddlewareTest(ModifyResponse, &testArgs{
middlewareOpt: opts, middlewareOpt: opts,
reqURL: reqURL, reqURL: reqURL,

View file

@ -13,7 +13,6 @@ type (
requestMap = map[string]*rate.Limiter requestMap = map[string]*rate.Limiter
rateLimiter struct { rateLimiter struct {
RateLimiterOpts RateLimiterOpts
Tracer
requestMap requestMap requestMap requestMap
mu sync.Mutex mu sync.Mutex
@ -51,7 +50,6 @@ func (rl *rateLimiter) newLimiter() *rate.Limiter {
func (rl *rateLimiter) limit(w http.ResponseWriter, r *http.Request) bool { func (rl *rateLimiter) limit(w http.ResponseWriter, r *http.Request) bool {
host, _, err := net.SplitHostPort(r.RemoteAddr) host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil { if err != nil {
rl.AddTracef("unable to parse remote address %s", r.RemoteAddr)
http.Error(w, "Internal error", http.StatusInternalServerError) http.Error(w, "Internal error", http.StatusInternalServerError)
return false return false
} }

View file

@ -5,21 +5,18 @@ import (
"net/http" "net/http"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
// https://nginx.org/en/docs/http/ngx_http_realip_module.html // https://nginx.org/en/docs/http/ngx_http_realip_module.html
type ( type (
realIP struct { realIP RealIPOpts
RealIPOpts
Tracer
}
RealIPOpts struct { RealIPOpts struct {
// Header is the name of the header to use for the real client IP // Header is the name of the header to use for the real client IP
Header string `validate:"required"` Header string `validate:"required"`
// From is a list of Address / CIDRs to trust // From is a list of Address / CIDRs to trust
From []*types.CIDR `validate:"required,min=1"` From []*nettypes.CIDR `validate:"required,min=1"`
/* /*
If recursive search is disabled, If recursive search is disabled,
the original client address that matches one of the trusted addresses is replaced by the original client address that matches one of the trusted addresses is replaced by
@ -36,13 +33,13 @@ var (
RealIP = NewMiddleware[realIP]() RealIP = NewMiddleware[realIP]()
realIPOptsDefault = RealIPOpts{ realIPOptsDefault = RealIPOpts{
Header: "X-Real-IP", Header: "X-Real-IP",
From: []*types.CIDR{}, From: []*nettypes.CIDR{},
} }
) )
// setup implements MiddlewareWithSetup. // setup implements MiddlewareWithSetup.
func (ri *realIP) setup() { func (ri *realIP) setup() {
ri.RealIPOpts = realIPOptsDefault *ri = realIP(realIPOptsDefault)
} }
// before implements RequestModifier. // before implements RequestModifier.
@ -77,7 +74,6 @@ func (ri *realIP) setRealIP(req *http.Request) {
} }
} }
if !isTrusted { if !isTrusted {
ri.AddTracef("client ip %s is not trusted", clientIP).With("allowed CIDRs", ri.From)
return return
} }
@ -90,7 +86,6 @@ func (ri *realIP) setRealIP(req *http.Request) {
} }
if len(realIPs) == 0 { if len(realIPs) == 0 {
ri.AddTracef("no real ip found in header %s", ri.Header).WithRequest(req)
return return
} }
@ -105,12 +100,10 @@ func (ri *realIP) setRealIP(req *http.Request) {
} }
if lastNonTrustedIP == "" { if lastNonTrustedIP == "" {
ri.AddTracef("no non-trusted ip found").With("allowed CIDRs", ri.From).With("ips", realIPs)
return return
} }
req.RemoteAddr = lastNonTrustedIP req.RemoteAddr = lastNonTrustedIP
req.Header.Set(ri.Header, lastNonTrustedIP) req.Header.Set(ri.Header, lastNonTrustedIP)
req.Header.Set(httpheaders.HeaderXRealIP, lastNonTrustedIP) req.Header.Set(httpheaders.HeaderXRealIP, lastNonTrustedIP)
ri.AddTracef("set real ip %s", lastNonTrustedIP)
} }

View file

@ -7,7 +7,7 @@ import (
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -23,7 +23,7 @@ func TestSetRealIPOpts(t *testing.T) {
} }
optExpected := &RealIPOpts{ optExpected := &RealIPOpts{
Header: httpheaders.HeaderXRealIP, Header: httpheaders.HeaderXRealIP,
From: []*types.CIDR{ From: []*nettypes.CIDR{
{ {
IP: net.ParseIP("127.0.0.0"), IP: net.ParseIP("127.0.0.0"),
Mask: net.IPv4Mask(255, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0),
@ -71,7 +71,6 @@ func TestSetRealIP(t *testing.T) {
result, err := newMiddlewareTest(mid, nil) result, err := newMiddlewareTest(mid, nil)
ExpectNoError(t, err) ExpectNoError(t, err)
t.Log(traces)
ExpectEqual(t, result.ResponseStatus, http.StatusOK) ExpectEqual(t, result.ResponseStatus, http.StatusOK)
ExpectEqual(t, strings.Split(result.RemoteAddr, ":")[0], testRealIP) ExpectEqual(t, strings.Split(result.RemoteAddr, ":")[0], testRealIP)
} }

View file

@ -4,13 +4,13 @@ import (
"net/http" "net/http"
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
func TestRedirectToHTTPs(t *testing.T) { func TestRedirectToHTTPs(t *testing.T) {
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{ result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
reqURL: types.MustParseURL("http://example.com"), reqURL: nettypes.MustParseURL("http://example.com"),
}) })
ExpectNoError(t, err) ExpectNoError(t, err)
ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect) ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect)
@ -19,7 +19,7 @@ func TestRedirectToHTTPs(t *testing.T) {
func TestNoRedirect(t *testing.T) { func TestNoRedirect(t *testing.T) {
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{ result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
reqURL: types.MustParseURL("https://example.com"), reqURL: nettypes.MustParseURL("https://example.com"),
}) })
ExpectNoError(t, err) ExpectNoError(t, err)
ExpectEqual(t, result.ResponseStatus, http.StatusOK) ExpectEqual(t, result.ResponseStatus, http.StatusOK)

View file

@ -11,7 +11,7 @@ import (
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -80,11 +80,11 @@ type TestResult struct {
type testArgs struct { type testArgs struct {
middlewareOpt OptionsRaw middlewareOpt OptionsRaw
upstreamURL *types.URL upstreamURL *nettypes.URL
realRoundTrip bool realRoundTrip bool
reqURL *types.URL reqURL *nettypes.URL
reqMethod string reqMethod string
headers http.Header headers http.Header
body []byte body []byte
@ -96,13 +96,13 @@ type testArgs struct {
func (args *testArgs) setDefaults() { func (args *testArgs) setDefaults() {
if args.reqURL == nil { if args.reqURL == nil {
args.reqURL = Must(types.ParseURL("https://example.com")) args.reqURL = Must(nettypes.ParseURL("https://example.com"))
} }
if args.reqMethod == "" { if args.reqMethod == "" {
args.reqMethod = http.MethodGet args.reqMethod = http.MethodGet
} }
if args.upstreamURL == nil { if args.upstreamURL == nil {
args.upstreamURL = Must(types.ParseURL("https://10.0.0.1:8443")) // dummy url, no actual effect args.upstreamURL = Must(nettypes.ParseURL("https://10.0.0.1:8443")) // dummy url, no actual effect
} }
if args.respHeaders == nil { if args.respHeaders == nil {
args.respHeaders = http.Header{} args.respHeaders = http.Header{}

View file

@ -1,87 +0,0 @@
package middleware
import (
"net/http"
"sync"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
)
type (
Trace struct {
Time string `json:"time,omitempty"`
Caller string `json:"caller,omitempty"`
URL string `json:"url,omitempty"`
Message string `json:"msg"`
ReqHeaders map[string]string `json:"req_headers,omitempty"`
RespHeaders map[string]string `json:"resp_headers,omitempty"`
RespStatus int `json:"resp_status,omitempty"`
Additional map[string]any `json:"additional,omitempty"`
}
Traces []*Trace
)
var (
traces = make(Traces, 0)
tracesMu sync.Mutex
)
const MaxTraceNum = 100
func GetAllTrace() []*Trace {
return traces
}
func (tr *Trace) WithRequest(req *http.Request) *Trace {
if tr == nil {
return nil
}
tr.URL = req.RequestURI
tr.ReqHeaders = httpheaders.HeaderToMap(req.Header)
return tr
}
func (tr *Trace) WithResponse(resp *http.Response) *Trace {
if tr == nil {
return nil
}
tr.URL = resp.Request.RequestURI
tr.ReqHeaders = httpheaders.HeaderToMap(resp.Request.Header)
tr.RespHeaders = httpheaders.HeaderToMap(resp.Header)
tr.RespStatus = resp.StatusCode
return tr
}
func (tr *Trace) With(what string, additional any) *Trace {
if tr == nil {
return nil
}
if tr.Additional == nil {
tr.Additional = map[string]any{}
}
tr.Additional[what] = additional
return tr
}
func (tr *Trace) WithError(err error) *Trace {
if tr == nil {
return nil
}
if tr.Additional == nil {
tr.Additional = map[string]any{}
}
tr.Additional["error"] = err.Error()
return tr
}
func addTrace(t *Trace) *Trace {
tracesMu.Lock()
defer tracesMu.Unlock()
if len(traces) > MaxTraceNum {
traces = traces[1:]
}
traces = append(traces, t)
return t
}

View file

@ -1,62 +0,0 @@
package middleware
import (
"fmt"
"net/http"
"time"
"github.com/yusing/go-proxy/internal/utils/strutils"
)
type Tracer struct {
name string
enabled bool
}
func _() {
var _ MiddlewareWithTracer = &Tracer{}
}
func (t *Tracer) enableTrace() {
t.enabled = true
}
func (t *Tracer) getTracer() *Tracer {
return t
}
func (t *Tracer) SetParent(parent *Tracer) {
if parent == nil {
return
}
t.name = parent.name + "." + t.name
}
func (t *Tracer) addTrace(msg string) *Trace {
return addTrace(&Trace{
Time: strutils.FormatTime(time.Now()),
Caller: t.name,
Message: msg,
})
}
func (t *Tracer) AddTracef(msg string, args ...any) *Trace {
if !t.enabled {
return nil
}
return t.addTrace(fmt.Sprintf(msg, args...))
}
func (t *Tracer) AddTraceRequest(msg string, req *http.Request) *Trace {
if !t.enabled {
return nil
}
return t.addTrace(msg).WithRequest(req)
}
func (t *Tracer) AddTraceResponse(msg string, resp *http.Response) *Trace {
if !t.enabled {
return nil
}
return t.addTrace(msg).WithResponse(resp)
}

View file

@ -28,7 +28,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/logging/accesslog" "github.com/yusing/go-proxy/internal/logging/accesslog"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
"golang.org/x/net/http/httpguts" "golang.org/x/net/http/httpguts"
) )
@ -93,7 +93,7 @@ type ReverseProxy struct {
HandlerFunc http.HandlerFunc HandlerFunc http.HandlerFunc
TargetName string TargetName string
TargetURL *types.URL TargetURL *nettypes.URL
} }
func singleJoiningSlash(a, b string) string { func singleJoiningSlash(a, b string) string {
@ -133,7 +133,7 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) {
// URLs to the scheme, host, and base path provided in target. If the // URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir", // target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir. // the target request will be for /base/dir.
func NewReverseProxy(name string, target *types.URL, transport http.RoundTripper) *ReverseProxy { func NewReverseProxy(name string, target *nettypes.URL, transport http.RoundTripper) *ReverseProxy {
if transport == nil { if transport == nil {
panic("nil transport") panic("nil transport")
} }
@ -271,7 +271,7 @@ func (p *ReverseProxy) handler(rw http.ResponseWriter, req *http.Request) {
return return
} }
req.Header.Del("Forwarded") outreq.Header.Del("Forwarded")
httpheaders.RemoveHopByHopHeaders(outreq.Header) httpheaders.RemoveHopByHopHeaders(outreq.Header)
// Issue 21096: tell backend applications that care about trailer support // Issue 21096: tell backend applications that care about trailer support
@ -312,7 +312,7 @@ func (p *ReverseProxy) handler(rw http.ResponseWriter, req *http.Request) {
} }
var reqScheme string var reqScheme string
if req.TLS != nil { if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
reqScheme = "https" reqScheme = "https"
} else { } else {
reqScheme = "http" reqScheme = "http"

View file

@ -3,6 +3,7 @@ package server
import ( import (
"context" "context"
"errors" "errors"
"net"
"net/http" "net/http"
"github.com/rs/zerolog" "github.com/rs/zerolog"
@ -10,7 +11,7 @@ import (
func convertError(err error) error { func convertError(err error) error {
switch { switch {
case err == nil, errors.Is(err, http.ErrServerClosed), errors.Is(err, context.Canceled): case err == nil, errors.Is(err, http.ErrServerClosed), errors.Is(err, context.Canceled), errors.Is(err, net.ErrClosed):
return nil return nil
default: default:
return err return err

View file

@ -3,6 +3,8 @@ package server
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"io"
"net" "net"
"net/http" "net/http"
"time" "time"
@ -105,7 +107,7 @@ func (s *Server) Start(parent task.Parent) {
} }
Start(subtask, h3, s.acl, &s.l) Start(subtask, h3, s.acl, &s.l)
if s.http != nil { if s.http != nil {
s.http.Handler = advertiseHTTP3(s.http.Handler, h3) s.http.Handler = advertiseHTTP3(s.http.Handler, h3)
} }
// s.https is not nil (checked above) // s.https is not nil (checked above)
s.https.Handler = advertiseHTTP3(s.https.Handler, h3) s.https.Handler = advertiseHTTP3(s.https.Handler, h3)
@ -115,7 +117,7 @@ func (s *Server) Start(parent task.Parent) {
Start(subtask, s.https, s.acl, &s.l) Start(subtask, s.https, s.acl, &s.l)
} }
func Start[Server httpServer](parent task.Parent, srv Server, acl *acl.Config, logger *zerolog.Logger) { func Start[Server httpServer](parent task.Parent, srv Server, acl *acl.Config, logger *zerolog.Logger) (port int) {
if srv == nil { if srv == nil {
return return
} }
@ -138,6 +140,7 @@ func Start[Server httpServer](parent task.Parent, srv Server, acl *acl.Config, l
HandleError(logger, err, "failed to listen on port") HandleError(logger, err, "failed to listen on port")
return return
} }
port = l.Addr().(*net.TCPAddr).Port
if srv.TLSConfig != nil { if srv.TLSConfig != nil {
l = tls.NewListener(l, srv.TLSConfig) l = tls.NewListener(l, srv.TLSConfig)
} }
@ -145,32 +148,36 @@ func Start[Server httpServer](parent task.Parent, srv Server, acl *acl.Config, l
l = acl.WrapTCP(l) l = acl.WrapTCP(l)
} }
serveFunc = getServeFunc(l, srv.Serve) serveFunc = getServeFunc(l, srv.Serve)
task.OnCancel("stop", func() {
stop(srv, l, logger)
})
case *http3.Server: case *http3.Server:
l, err := lc.ListenPacket(task.Context(), "udp", srv.Addr) l, err := lc.ListenPacket(task.Context(), "udp", srv.Addr)
if err != nil { if err != nil {
HandleError(logger, err, "failed to listen on port") HandleError(logger, err, "failed to listen on port")
return return
} }
port = l.LocalAddr().(*net.UDPAddr).Port
if acl != nil { if acl != nil {
l = acl.WrapUDP(l) l = acl.WrapUDP(l)
} }
serveFunc = getServeFunc(l, srv.Serve) serveFunc = getServeFunc(l, srv.Serve)
task.OnCancel("stop", func() {
stop(srv, l, logger)
})
} }
task.OnCancel("stop", func() {
stop(srv, logger)
})
logStarted(srv, logger) logStarted(srv, logger)
go func() { go func() {
err := convertError(serveFunc()) err := convertError(serveFunc())
if err != nil { if err != nil {
HandleError(logger, err, "failed to serve "+proto+" server") HandleError(logger, err, "failed to serve "+proto+" server")
} }
task.Finish(err) task.Finish(err)
}() }()
return port return port
} }
func stop[Server httpServer](srv Server, logger *zerolog.Logger) { func stop[Server httpServer](srv Server, l io.Closer, logger *zerolog.Logger) {
if srv == nil { if srv == nil {
return return
} }
@ -180,7 +187,7 @@ func stop[Server httpServer](srv Server, logger *zerolog.Logger) {
ctx, cancel := context.WithTimeout(task.RootContext(), 1*time.Second) ctx, cancel := context.WithTimeout(task.RootContext(), 1*time.Second)
defer cancel() defer cancel()
if err := convertError(srv.Shutdown(ctx)); err != nil { if err := convertError(errors.Join(srv.Shutdown(ctx), l.Close())); err != nil {
HandleError(logger, err, "failed to shutdown "+proto+" server") HandleError(logger, err, "failed to shutdown "+proto+" server")
} else { } else {
logger.Info().Str("proto", proto).Str("addr", addr(srv)).Msg("server stopped") logger.Info().Str("proto", proto).Str("addr", addr(srv)).Msg("server stopped")

View file

@ -1,4 +1,4 @@
package types package nettypes
import ( import (
"net" "net"

View file

@ -1,32 +1,14 @@
package types package nettypes
import ( import (
"fmt" "context"
"net" "net"
) )
type ( type Stream interface {
Stream interface { ListenAndServe(ctx context.Context, preDial, onRead HookFunc)
fmt.Stringer LocalAddr() net.Addr
StreamListener Close() error
Setup() error
Handle(conn StreamConn) error
}
StreamListener interface {
Addr() net.Addr
Accept() (StreamConn, error)
Close() error
}
StreamConn any
NetListenerWrapper struct {
net.Listener
}
)
func NetListener(l net.Listener) StreamListener {
return NetListenerWrapper{Listener: l}
} }
func (l NetListenerWrapper) Accept() (StreamConn, error) { type HookFunc func(ctx context.Context) error
return l.Listener.Accept()
}

View file

@ -1,4 +1,4 @@
package types package nettypes
import ( import (
urlPkg "net/url" urlPkg "net/url"

View file

@ -86,7 +86,7 @@ func (f FieldsBody) Format(format *LogFormat) ([]byte, error) {
case LogFormatRawJSON: case LogFormatRawJSON:
return json.Marshal(f) return json.Marshal(f)
} }
return nil, fmt.Errorf("unknown format: %v", format) return f.Format(LogFormatMarkdown)
} }
func (l ListBody) Format(format *LogFormat) ([]byte, error) { func (l ListBody) Format(format *LogFormat) ([]byte, error) {
@ -104,7 +104,7 @@ func (l ListBody) Format(format *LogFormat) ([]byte, error) {
case LogFormatRawJSON: case LogFormatRawJSON:
return json.Marshal(l) return json.Marshal(l)
} }
return nil, fmt.Errorf("unknown format: %v", format) return l.Format(LogFormatMarkdown)
} }
func (m MessageBody) Format(format *LogFormat) ([]byte, error) { func (m MessageBody) Format(format *LogFormat) ([]byte, error) {
@ -114,13 +114,13 @@ func (m MessageBody) Format(format *LogFormat) ([]byte, error) {
case LogFormatRawJSON: case LogFormatRawJSON:
return json.Marshal(m) return json.Marshal(m)
} }
return nil, fmt.Errorf("unknown format: %v", format) return m.Format(LogFormatMarkdown)
} }
func (e ErrorBody) Format(format *LogFormat) ([]byte, error) { func (e ErrorBody) Format(format *LogFormat) ([]byte, error) {
switch format { switch format {
case LogFormatRawJSON: case LogFormatRawJSON:
return json.Marshal(e) return json.Marshal(e.Error)
case LogFormatPlain: case LogFormatPlain:
return gperr.Plain(e.Error), nil return gperr.Plain(e.Error), nil
case LogFormatMarkdown: case LogFormatMarkdown:

View file

@ -1,10 +1,12 @@
package notif package notif
import ( import (
"math"
"math/rand/v2"
"time" "time"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/yusing/go-proxy/internal/gperr" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
F "github.com/yusing/go-proxy/internal/utils/functional" F "github.com/yusing/go-proxy/internal/utils/functional"
) )
@ -14,7 +16,7 @@ type (
task *task.Task task *task.Task
providers F.Set[Provider] providers F.Set[Provider]
logCh chan *LogMessage logCh chan *LogMessage
retryCh chan *RetryMessage retryMsg F.Set[*RetryMessage]
retryTicker *time.Ticker retryTicker *time.Ticker
} }
LogMessage struct { LogMessage struct {
@ -23,32 +25,22 @@ type (
Body LogBody Body LogBody
Color Color Color Color
} }
RetryMessage struct {
Message *LogMessage
Trials int
Provider Provider
}
) )
var dispatcher *Dispatcher var dispatcher *Dispatcher
const retryInterval = 5 * time.Second const (
retryInterval = time.Second
var maxRetries = map[zerolog.Level]int{ maxBackoffDelay = 5 * time.Minute
zerolog.DebugLevel: 1, backoffMultiplier = 2.0
zerolog.InfoLevel: 1, )
zerolog.WarnLevel: 3,
zerolog.ErrorLevel: 5,
zerolog.FatalLevel: 10,
zerolog.PanicLevel: 10,
}
func StartNotifDispatcher(parent task.Parent) *Dispatcher { func StartNotifDispatcher(parent task.Parent) *Dispatcher {
dispatcher = &Dispatcher{ dispatcher = &Dispatcher{
task: parent.Subtask("notification", true), task: parent.Subtask("notification", true),
providers: F.NewSet[Provider](), providers: F.NewSet[Provider](),
logCh: make(chan *LogMessage), logCh: make(chan *LogMessage, 100),
retryCh: make(chan *RetryMessage, 100), retryMsg: F.NewSet[*RetryMessage](),
retryTicker: time.NewTicker(retryInterval), retryTicker: time.NewTicker(retryInterval),
} }
go dispatcher.start() go dispatcher.start()
@ -73,11 +65,10 @@ func (disp *Dispatcher) RegisterProvider(cfg *NotificationConfig) {
func (disp *Dispatcher) start() { func (disp *Dispatcher) start() {
defer func() { defer func() {
dispatcher = nil
disp.providers.Clear() disp.providers.Clear()
close(disp.logCh) close(disp.logCh)
close(disp.retryCh)
disp.task.Finish(nil) disp.task.Finish(nil)
dispatcher = nil
}() }()
for { for {
@ -90,22 +81,7 @@ func (disp *Dispatcher) start() {
} }
go disp.dispatch(msg) go disp.dispatch(msg)
case <-disp.retryTicker.C: case <-disp.retryTicker.C:
if len(disp.retryCh) == 0 { disp.processRetries()
continue
}
var msgs []*RetryMessage
done := false
for !done {
select {
case msg := <-disp.retryCh:
msgs = append(msgs, msg)
default:
done = true
}
}
if err := disp.retry(msgs); err != nil {
gperr.LogError("notification retry failed", err)
}
} }
} }
} }
@ -114,34 +90,100 @@ func (disp *Dispatcher) dispatch(msg *LogMessage) {
task := disp.task.Subtask("dispatcher", true) task := disp.task.Subtask("dispatcher", true)
defer task.Finish("notif dispatched") defer task.Finish("notif dispatched")
l := log.With().
Str("level", msg.Level.String()).
Str("title", msg.Title).Logger()
disp.providers.RangeAllParallel(func(p Provider) { disp.providers.RangeAllParallel(func(p Provider) {
if err := msg.notify(task.Context(), p); err != nil { if err := msg.notify(task.Context(), p); err != nil {
disp.retryCh <- &RetryMessage{ msg := &RetryMessage{
Message: msg, Message: msg,
Trials: 0, Trials: 0,
Provider: p, Provider: p,
NextRetry: time.Now().Add(calculateBackoffDelay(0)),
} }
disp.retryMsg.Add(msg)
l.Debug().Err(err).EmbedObject(msg).Msg("notification failed, scheduling retry")
} else {
l.Debug().Str("provider", p.GetName()).Msg("notification sent successfully")
} }
}) })
} }
func (disp *Dispatcher) retry(messages []*RetryMessage) error { func (disp *Dispatcher) processRetries() {
if disp.retryMsg.Size() == 0 {
return
}
now := time.Now()
readyMessages := make([]*RetryMessage, 0)
for msg := range disp.retryMsg.Range {
if now.After(msg.NextRetry) {
readyMessages = append(readyMessages, msg)
disp.retryMsg.Remove(msg)
}
}
disp.retry(readyMessages)
}
func (disp *Dispatcher) retry(messages []*RetryMessage) {
if len(messages) == 0 {
return
}
task := disp.task.Subtask("retry", true) task := disp.task.Subtask("retry", true)
defer task.Finish("notif retried") defer task.Finish("notif retried")
errs := gperr.NewBuilder("notification failure") successCount := 0
failureCount := 0
for _, msg := range messages { for _, msg := range messages {
maxTrials := maxRetries[msg.Message.Level]
log.Debug().EmbedObject(msg).Msg("attempting notification retry")
err := msg.Message.notify(task.Context(), msg.Provider) err := msg.Message.notify(task.Context(), msg.Provider)
if err == nil { if err == nil {
msg.NextRetry = time.Time{}
successCount++
log.Debug().EmbedObject(msg).Msg("notification retry succeeded")
continue continue
} }
if msg.Trials > maxRetries[msg.Message.Level] {
errs.Addf("notification provider %s failed after %d trials", msg.Provider.GetName(), msg.Trials)
errs.Add(err)
continue
}
msg.Trials++ msg.Trials++
disp.retryCh <- msg failureCount++
if msg.Trials >= maxTrials {
log.Warn().Err(err).EmbedObject(msg).Msg("notification permanently failed after max retries")
continue
}
// Schedule next retry with exponential backoff
msg.NextRetry = time.Now().Add(calculateBackoffDelay(msg.Trials))
disp.retryMsg.Add(msg)
log.Debug().EmbedObject(msg).Msg("notification retry failed, scheduled for later")
} }
return errs.Error()
log.Info().
Int("total", len(messages)).
Int("successes", successCount).
Int("failures", failureCount).
Msg("notification retry batch completed")
}
// calculateBackoffDelay implements exponential backoff with jitter.
func calculateBackoffDelay(trials int) time.Duration {
if trials == 0 {
return retryInterval
}
// Exponential backoff: retryInterval * (backoffMultiplier ^ trials)
delay := min(float64(retryInterval)*math.Pow(backoffMultiplier, float64(trials)), float64(maxBackoffDelay))
// Add 20% jitter to prevent thundering herd
//nolint:gosec
jitter := delay * 0.2 * (rand.Float64() - 0.5) // -10% to +10%
return time.Duration(delay + jitter)
} }

View file

@ -8,7 +8,6 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/serialization" "github.com/yusing/go-proxy/internal/serialization"
) )
@ -72,13 +71,6 @@ func (msg *LogMessage) notify(ctx context.Context, provider Provider) error {
switch resp.StatusCode { switch resp.StatusCode {
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent: case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent:
body, _ := io.ReadAll(resp.Body)
log.Debug().
Str("provider", provider.GetName()).
Str("url", provider.GetURL()).
Str("status", resp.Status).
RawJSON("resp_body", body).
Msg("notification sent")
return nil return nil
default: default:
return fmt.Errorf("http status %d: %w", resp.StatusCode, provider.fmtError(resp.Body)) return fmt.Errorf("http status %d: %w", resp.StatusCode, provider.fmtError(resp.Body))

View file

@ -0,0 +1,33 @@
package notif
import (
"time"
"github.com/rs/zerolog"
)
type RetryMessage struct {
Message *LogMessage
Trials int
Provider Provider
NextRetry time.Time
}
var maxRetries = map[zerolog.Level]int{
zerolog.DebugLevel: 1,
zerolog.InfoLevel: 1,
zerolog.WarnLevel: 3,
zerolog.ErrorLevel: 5,
zerolog.FatalLevel: 10,
zerolog.PanicLevel: 10,
}
func (msg *RetryMessage) MarshalZerologObject(e *zerolog.Event) {
e.Str("provider", msg.Provider.GetName()).
Int("trial", msg.Trials+1).
Str("title", msg.Message.Title)
if !msg.NextRetry.IsZero() {
e.Int("max_retries", maxRetries[msg.Message.Level]).
Time("next_retry", msg.NextRetry)
}
}

26
internal/route/common.go Normal file
View file

@ -0,0 +1,26 @@
package route
import (
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/route/routes"
)
func checkExists(r routes.Route) gperr.Error {
if r.UseLoadBalance() { // skip checking for load balanced routes
return nil
}
var (
existing routes.Route
ok bool
)
switch r := r.(type) {
case routes.HTTPRoute:
existing, ok = routes.HTTP.Get(r.Key())
case routes.StreamRoute:
existing, ok = routes.Stream.Get(r.Key())
}
if ok {
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
}
return nil
}

View file

@ -11,7 +11,6 @@ import (
"github.com/yusing/go-proxy/internal/net/gphttp/middleware" "github.com/yusing/go-proxy/internal/net/gphttp/middleware"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
) )
@ -19,9 +18,6 @@ type (
FileServer struct { FileServer struct {
*Route *Route
Health *monitor.FileServerHealthMonitor `json:"health"`
task *task.Task
middleware *middleware.Middleware middleware *middleware.Middleware
handler http.Handler handler http.Handler
accessLogger *accesslog.AccessLogger accessLogger *accesslog.AccessLogger
@ -90,28 +86,27 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
} }
if s.UseHealthCheck() { if s.UseHealthCheck() {
s.Health = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root) s.HealthMon = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
if err := s.Health.Start(s.task); err != nil { if err := s.HealthMon.Start(s.task); err != nil {
return err return err
} }
} }
if s.ShouldExclude() {
return nil
}
if err := checkExists(s); err != nil {
return err
}
routes.HTTP.Add(s) routes.HTTP.Add(s)
s.task.OnCancel("entrypoint_remove_route", func() { s.task.OnFinished("remove_route_from_http", func() {
routes.HTTP.Del(s) routes.HTTP.Del(s)
}) })
return nil return nil
} }
func (s *FileServer) Task() *task.Task {
return s.task
}
// Finish implements task.TaskFinisher.
func (s *FileServer) Finish(reason any) {
s.task.Finish(reason)
}
// ServeHTTP implements http.Handler. // ServeHTTP implements http.Handler.
func (s *FileServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (s *FileServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.handler.ServeHTTP(w, req) s.handler.ServeHTTP(w, req)
@ -119,7 +114,3 @@ func (s *FileServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.accessLogger.Log(req, req.Response) s.accessLogger.Log(req, req.Response)
} }
} }
func (s *FileServer) HealthMonitor() health.HealthMonitor {
return s.Health
}

View file

@ -6,6 +6,8 @@ var (
"mysql": 3306, "mysql": 3306,
"mariadb": 3306, "mariadb": 3306,
"postgres": 5432, "postgres": 5432,
"pgvecto-rs": 5432,
"pgvector": 5432,
"rabbitmq": 5672, "rabbitmq": 5672,
"redis": 6379, "redis": 6379,
"memcached": 11211, "memcached": 11211,

View file

@ -71,7 +71,9 @@ func (p *DockerProvider) loadRoutesImpl() (route.Routes, gperr.Error) {
for _, c := range containers { for _, c := range containers {
container := docker.FromDocker(&c, p.dockerHost) container := docker.FromDocker(&c, p.dockerHost)
if container.IsExcluded {
if container.Errors != nil {
errs.Add(container.Errors)
continue continue
} }
@ -89,10 +91,15 @@ func (p *DockerProvider) loadRoutesImpl() (route.Routes, gperr.Error) {
} }
for k, v := range newEntries { for k, v := range newEntries {
if conflict, ok := routes[k]; ok { if conflict, ok := routes[k]; ok {
errs.Add(gperr.Multiline(). err := gperr.Multiline().
Addf("route with alias %s already exists", k). Addf("route with alias %s already exists", k).
Addf("container %s", container.ContainerName). Addf("container %s", container.ContainerName).
Addf("conflicting container %s", conflict.Container.ContainerName)) Addf("conflicting container %s", conflict.Container.ContainerName)
if conflict.ShouldExclude() || v.ShouldExclude() {
gperr.LogWarn("skipping conflicting route", err)
} else {
errs.Add(err)
}
} else { } else {
routes[k] = v routes[k] = v
} }
@ -122,11 +129,9 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
errs := gperr.NewBuilder("label errors") errs := gperr.NewBuilder("label errors")
m, err := docker.ParseLabels(container.Labels) m, err := docker.ParseLabels(container.Labels, container.Aliases...)
errs.Add(err) errs.Add(err)
var wildcardProps docker.LabelMap
for alias, entryMapAny := range m { for alias, entryMapAny := range m {
if len(alias) == 0 { if len(alias) == 0 {
errs.Add(gperr.New("empty alias")) errs.Add(gperr.New("empty alias"))
@ -148,11 +153,6 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
} }
} }
if alias == docker.WildcardAlias {
wildcardProps = entryMap
continue
}
// check if it is an alias reference // check if it is an alias reference
switch alias[0] { switch alias[0] {
case aliasRefPrefix, aliasRefPrefixAlt: case aliasRefPrefix, aliasRefPrefixAlt:
@ -187,14 +187,6 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
routes[alias] = r routes[alias] = r
} }
} }
if wildcardProps != nil {
for _, re := range routes {
if err := serialization.MapUnmarshalValidate(wildcardProps, re); err != nil {
errs.Add(err.Subject(docker.WildcardAlias))
break
}
}
}
return routes, errs.Error() return routes, errs.Error()
} }

View file

@ -4,7 +4,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
D "github.com/yusing/go-proxy/internal/docker" D "github.com/yusing/go-proxy/internal/docker"
@ -319,7 +318,7 @@ func TestStreamDefaultValues(t *testing.T) {
}, },
}, },
}, },
Ports: []types.Port{ Ports: []container.Port{
{Type: "udp", PrivatePort: privPort, PublicPort: pubPort}, {Type: "udp", PrivatePort: privPort, PublicPort: pubPort},
}, },
} }
@ -372,7 +371,7 @@ func TestImplicitExcludeDatabase(t *testing.T) {
t.Run("exposed port detection", func(t *testing.T) { t.Run("exposed port detection", func(t *testing.T) {
r, ok := makeRoutes(&container.SummaryTrimmed{ r, ok := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames, Names: dummyNames,
Ports: []types.Port{ Ports: []container.Port{
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432}, {Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
}, },
})["a"] })["a"]

View file

@ -3,7 +3,7 @@ package provider
import ( import (
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/route" "github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/provider/types" provider "github.com/yusing/go-proxy/internal/route/provider/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher" "github.com/yusing/go-proxy/internal/watcher"
eventsPkg "github.com/yusing/go-proxy/internal/watcher/events" eventsPkg "github.com/yusing/go-proxy/internal/watcher/events"
@ -23,7 +23,7 @@ func (p *Provider) newEventHandler() *EventHandler {
} }
func (handler *EventHandler) Handle(parent task.Parent, events []watcher.Event) { func (handler *EventHandler) Handle(parent task.Parent, events []watcher.Event) {
oldRoutes := handler.provider.routes oldRoutes := handler.provider.lockCloneRoutes()
isForceReload := false isForceReload := false
for _, event := range events { for _, event := range events {
@ -68,10 +68,10 @@ func (handler *EventHandler) matchAny(events []watcher.Event, route *route.Route
func (handler *EventHandler) match(event watcher.Event, route *route.Route) bool { func (handler *EventHandler) match(event watcher.Event, route *route.Route) bool {
switch handler.provider.GetType() { switch handler.provider.GetType() {
case types.ProviderTypeDocker, types.ProviderTypeAgent: case provider.ProviderTypeDocker, provider.ProviderTypeAgent:
return route.Container.ContainerID == event.ActorID || return route.Container.ContainerID == event.ActorID ||
route.Container.ContainerName == event.ActorName route.Container.ContainerName == event.ActorName
case types.ProviderTypeFile: case provider.ProviderTypeFile:
return true return true
} }
// should never happen // should never happen
@ -86,12 +86,11 @@ func (handler *EventHandler) Add(parent task.Parent, route *route.Route) {
} }
func (handler *EventHandler) Remove(route *route.Route) { func (handler *EventHandler) Remove(route *route.Route) {
route.Finish("route removed") route.FinishAndWait("route removed")
delete(handler.provider.routes, route.Alias)
} }
func (handler *EventHandler) Update(parent task.Parent, oldRoute *route.Route, newRoute *route.Route) { func (handler *EventHandler) Update(parent task.Parent, oldRoute *route.Route, newRoute *route.Route) {
oldRoute.Finish("route update") oldRoute.FinishAndWait("route update")
err := handler.provider.startRoute(parent, newRoute) err := handler.provider.startRoute(parent, newRoute)
if err != nil { if err != nil {
handler.errs.Add(err.Subject("update")) handler.errs.Add(err.Subject("update"))

View file

@ -33,8 +33,17 @@ func FileProviderImpl(filename string) (ProviderImpl, error) {
return impl, nil return impl, nil
} }
func removeXPrefix(m map[string]any) gperr.Error {
for alias := range m {
if strings.HasPrefix(alias, "x-") {
delete(m, alias)
}
}
return nil
}
func validate(data []byte) (routes route.Routes, err gperr.Error) { func validate(data []byte) (routes route.Routes, err gperr.Error) {
err = serialization.UnmarshalValidateYAML(data, &routes) err = serialization.UnmarshalValidateYAMLIntercept(data, &routes, removeXPrefix)
return return
} }

View file

@ -3,14 +3,17 @@ package provider
import ( import (
"errors" "errors"
"fmt" "fmt"
"maps"
"path" "path"
"sync"
"time" "time"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/route" "github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/provider/types" provider "github.com/yusing/go-proxy/internal/route/provider/types"
"github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
W "github.com/yusing/go-proxy/internal/watcher" W "github.com/yusing/go-proxy/internal/watcher"
"github.com/yusing/go-proxy/internal/watcher/events" "github.com/yusing/go-proxy/internal/watcher/events"
@ -20,8 +23,9 @@ type (
Provider struct { Provider struct {
ProviderImpl ProviderImpl
t types.ProviderType t provider.Type
routes route.Routes routes route.Routes
routesMu sync.RWMutex
watcher W.Watcher watcher W.Watcher
} }
@ -41,7 +45,9 @@ const (
var ErrEmptyProviderName = errors.New("empty provider name") var ErrEmptyProviderName = errors.New("empty provider name")
func newProvider(t types.ProviderType) *Provider { var _ routes.Provider = (*Provider)(nil)
func newProvider(t provider.Type) *Provider {
return &Provider{t: t} return &Provider{t: t}
} }
@ -50,7 +56,7 @@ func NewFileProvider(filename string) (p *Provider, err error) {
if name == "" { if name == "" {
return nil, ErrEmptyProviderName return nil, ErrEmptyProviderName
} }
p = newProvider(types.ProviderTypeFile) p = newProvider(provider.ProviderTypeFile)
p.ProviderImpl, err = FileProviderImpl(filename) p.ProviderImpl, err = FileProviderImpl(filename)
if err != nil { if err != nil {
return nil, err return nil, err
@ -60,14 +66,14 @@ func NewFileProvider(filename string) (p *Provider, err error) {
} }
func NewDockerProvider(name string, dockerHost string) *Provider { func NewDockerProvider(name string, dockerHost string) *Provider {
p := newProvider(types.ProviderTypeDocker) p := newProvider(provider.ProviderTypeDocker)
p.ProviderImpl = DockerProviderImpl(name, dockerHost) p.ProviderImpl = DockerProviderImpl(name, dockerHost)
p.watcher = p.NewWatcher() p.watcher = p.NewWatcher()
return p return p
} }
func NewAgentProvider(cfg *agent.AgentConfig) *Provider { func NewAgentProvider(cfg *agent.AgentConfig) *Provider {
p := newProvider(types.ProviderTypeAgent) p := newProvider(provider.ProviderTypeAgent)
agent := &AgentProvider{ agent := &AgentProvider{
AgentConfig: cfg, AgentConfig: cfg,
docker: DockerProviderImpl(cfg.Name(), cfg.FakeDockerHost()), docker: DockerProviderImpl(cfg.Name(), cfg.FakeDockerHost()),
@ -77,7 +83,7 @@ func NewAgentProvider(cfg *agent.AgentConfig) *Provider {
return p return p
} }
func (p *Provider) GetType() types.ProviderType { func (p *Provider) GetType() provider.Type {
return p.t return p.t
} }
@ -86,25 +92,29 @@ func (p *Provider) MarshalText() ([]byte, error) {
return []byte(p.String()), nil return []byte(p.String()), nil
} }
func (p *Provider) startRoute(parent task.Parent, r *route.Route) gperr.Error {
err := r.Start(parent)
if err != nil {
delete(p.routes, r.Alias)
return err.Subject(r.Alias)
}
p.routes[r.Alias] = r
return nil
}
// Start implements task.TaskStarter. // Start implements task.TaskStarter.
func (p *Provider) Start(parent task.Parent) gperr.Error { func (p *Provider) Start(parent task.Parent) gperr.Error {
errs := gperr.NewBuilder("routes error")
errs.EnableConcurrency()
t := parent.Subtask("provider."+p.String(), false) t := parent.Subtask("provider."+p.String(), false)
errs := gperr.NewBuilder("routes error") // no need to lock here because we are not modifying the routes map.
routeSlice := make([]*route.Route, 0, len(p.routes))
for _, r := range p.routes { for _, r := range p.routes {
errs.Add(p.startRoute(t, r)) routeSlice = append(routeSlice, r)
} }
var wg sync.WaitGroup
for _, r := range routeSlice {
wg.Add(1)
go func(r *route.Route) {
defer wg.Done()
errs.Add(p.startRoute(t, r))
}(r)
}
wg.Wait()
eventQueue := events.NewEventQueue( eventQueue := events.NewEventQueue(
t.Subtask("event_queue", false), t.Subtask("event_queue", false),
providerEventFlushInterval, providerEventFlushInterval,
@ -126,15 +136,52 @@ func (p *Provider) Start(parent task.Parent) gperr.Error {
return nil return nil
} }
func (p *Provider) RangeRoutes(do func(string, *route.Route)) { func (p *Provider) LoadRoutes() (err gperr.Error) {
for alias, r := range p.routes { p.routes, err = p.loadRoutes()
do(alias, r) return
}
func (p *Provider) NumRoutes() int {
return len(p.routes)
}
func (p *Provider) IterRoutes(yield func(string, routes.Route) bool) {
routes := p.lockCloneRoutes()
for alias, r := range routes {
if !yield(alias, r.Impl()) {
break
}
} }
} }
func (p *Provider) GetRoute(alias string) (r *route.Route, ok bool) { func (p *Provider) FindService(project, service string) (routes.Route, bool) {
r, ok = p.routes[alias] switch p.GetType() {
return case provider.ProviderTypeDocker, provider.ProviderTypeAgent:
default:
return nil, false
}
if project == "" || service == "" {
return nil, false
}
routes := p.lockCloneRoutes()
for _, r := range routes {
cont := r.ContainerInfo()
if cont.DockerComposeProject() != project {
continue
}
if cont.DockerComposeService() == service {
return r.Impl(), true
}
}
return nil, false
}
func (p *Provider) GetRoute(alias string) (routes.Route, bool) {
r, ok := p.lockGetRoute(alias)
if !ok {
return nil, false
}
return r.Impl(), true
} }
func (p *Provider) loadRoutes() (routes route.Routes, err gperr.Error) { func (p *Provider) loadRoutes() (routes route.Routes, err gperr.Error) {
@ -148,26 +195,51 @@ func (p *Provider) loadRoutes() (routes route.Routes, err gperr.Error) {
// set alias and provider, then validate // set alias and provider, then validate
for alias, r := range routes { for alias, r := range routes {
r.Alias = alias r.Alias = alias
r.Provider = p.ShortName() r.SetProvider(p)
if err := r.Validate(); err != nil { if err := r.Validate(); err != nil {
errs.Add(err.Subject(alias)) errs.Add(err.Subject(alias))
delete(routes, alias) delete(routes, alias)
continue continue
} }
if r.ShouldExclude() {
delete(routes, alias)
continue
}
r.FinalizeHomepageConfig() r.FinalizeHomepageConfig()
} }
return routes, errs.Error() return routes, errs.Error()
} }
func (p *Provider) LoadRoutes() (err gperr.Error) { func (p *Provider) startRoute(parent task.Parent, r *route.Route) gperr.Error {
p.routes, err = p.loadRoutes() err := r.Start(parent)
return if err != nil {
p.lockDeleteRoute(r.Alias)
return err.Subject(r.Alias)
}
p.lockAddRoute(r)
r.Task().OnCancel("remove_route_from_provider", func() {
p.lockDeleteRoute(r.Alias)
})
return nil
} }
func (p *Provider) NumRoutes() int { func (p *Provider) lockAddRoute(r *route.Route) {
return len(p.routes) p.routesMu.Lock()
defer p.routesMu.Unlock()
p.routes[r.Alias] = r
}
func (p *Provider) lockDeleteRoute(alias string) {
p.routesMu.Lock()
defer p.routesMu.Unlock()
delete(p.routes, alias)
}
func (p *Provider) lockGetRoute(alias string) (*route.Route, bool) {
p.routesMu.RLock()
defer p.routesMu.RUnlock()
r, ok := p.routes[alias]
return r, ok
}
func (p *Provider) lockCloneRoutes() route.Routes {
p.routesMu.RLock()
defer p.routesMu.RUnlock()
return maps.Clone(p.routes)
} }

View file

@ -2,7 +2,7 @@ package provider
import ( import (
R "github.com/yusing/go-proxy/internal/route" R "github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/provider/types" provider "github.com/yusing/go-proxy/internal/route/provider/types"
route "github.com/yusing/go-proxy/internal/route/types" route "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
) )
@ -17,10 +17,10 @@ type (
NumUnknown uint16 `json:"unknown"` NumUnknown uint16 `json:"unknown"`
} }
ProviderStats struct { ProviderStats struct {
Total uint16 `json:"total"` Total uint16 `json:"total"`
RPs RouteStats `json:"reverse_proxies"` RPs RouteStats `json:"reverse_proxies"`
Streams RouteStats `json:"streams"` Streams RouteStats `json:"streams"`
Type types.ProviderType `json:"type"` Type provider.Type `json:"type"`
} }
) )

View file

@ -1,9 +1,9 @@
package types package provider
type ProviderType string type Type string
const ( const (
ProviderTypeDocker ProviderType = "docker" ProviderTypeDocker Type = "docker"
ProviderTypeFile ProviderType = "file" ProviderTypeFile Type = "file"
ProviderTypeAgent ProviderType = "agent" ProviderTypeAgent Type = "agent"
) )

View file

@ -3,6 +3,7 @@ package route
import ( import (
"crypto/tls" "crypto/tls"
"net/http" "net/http"
"sync"
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/agent/pkg/agentproxy" "github.com/yusing/go-proxy/agent/pkg/agentproxy"
@ -15,23 +16,18 @@ import (
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types" loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/net/gphttp/middleware" "github.com/yusing/go-proxy/internal/net/gphttp/middleware"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
) )
type ReveseProxyRoute struct { type ReveseProxyRoute struct {
*Route *Route
HealthMon health.HealthMonitor `json:"health,omitempty"`
loadBalancer *loadbalancer.LoadBalancer loadBalancer *loadbalancer.LoadBalancer
handler http.Handler handler http.Handler
rp *reverseproxy.ReverseProxy rp *reverseproxy.ReverseProxy
task *task.Task
} }
var _ routes.ReverseProxyRoute = (*ReveseProxyRoute)(nil) var _ routes.ReverseProxyRoute = (*ReveseProxyRoute)(nil)
@ -43,18 +39,21 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
proxyURL := base.ProxyURL proxyURL := base.ProxyURL
var trans *http.Transport var trans *http.Transport
a := base.Agent() a := base.GetAgent()
if a != nil { if a != nil {
trans = a.Transport() trans = a.Transport()
proxyURL = types.NewURL(agent.HTTPProxyURL) proxyURL = nettypes.NewURL(agent.HTTPProxyURL)
} else { } else {
trans = gphttp.NewTransport() trans = gphttp.NewTransport()
if httpConfig.NoTLSVerify { if httpConfig.NoTLSVerify {
trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec
} }
if httpConfig.ResponseHeaderTimeout > 0 { if httpConfig.ResponseHeaderTimeout > 0 {
trans.ResponseHeaderTimeout = httpConfig.ResponseHeaderTimeout trans.ResponseHeaderTimeout = httpConfig.ResponseHeaderTimeout
} }
if httpConfig.DisableCompression {
trans.DisableCompression = true
}
} }
service := base.Name() service := base.Name()
@ -95,14 +94,11 @@ func (r *ReveseProxyRoute) ReverseProxy() *reverseproxy.ReverseProxy {
// Start implements task.TaskStarter. // Start implements task.TaskStarter.
func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error { func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
if existing, ok := routes.HTTP.Get(r.Key()); ok && !r.UseLoadBalance() {
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
}
r.task = parent.Subtask("http."+r.Name(), false) r.task = parent.Subtask("http."+r.Name(), false)
switch { switch {
case r.UseIdleWatcher(): case r.UseIdleWatcher():
waker, err := idlewatcher.NewWatcher(parent, r) waker, err := idlewatcher.NewWatcher(parent, r, r.IdlewatcherConfig())
if err != nil { if err != nil {
r.task.Finish(err) r.task.Finish(err)
return gperr.Wrap(err) return gperr.Wrap(err)
@ -136,11 +132,19 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
} }
} }
if r.ShouldExclude() {
return nil
}
if err := checkExists(r); err != nil {
return err
}
if r.UseLoadBalance() { if r.UseLoadBalance() {
r.addToLoadBalancer(parent) r.addToLoadBalancer(parent)
} else { } else {
routes.HTTP.Add(r) routes.HTTP.Add(r)
r.task.OnFinished("entrypoint_remove_route", func() { r.task.OnCancel("remove_route_from_http", func() {
routes.HTTP.Del(r) routes.HTTP.Del(r)
}) })
} }
@ -149,30 +153,21 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
return nil return nil
} }
// Task implements task.TaskStarter.
func (r *ReveseProxyRoute) Task() *task.Task {
return r.task
}
// Finish implements task.TaskFinisher.
func (r *ReveseProxyRoute) Finish(reason any) {
r.task.Finish(reason)
}
func (r *ReveseProxyRoute) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (r *ReveseProxyRoute) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.handler.ServeHTTP(w, req) r.handler.ServeHTTP(w, req)
} }
func (r *ReveseProxyRoute) HealthMonitor() health.HealthMonitor { var lbLock sync.Mutex
return r.HealthMon
}
func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) { func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
var lb *loadbalancer.LoadBalancer var lb *loadbalancer.LoadBalancer
cfg := r.LoadBalance cfg := r.LoadBalance
lbLock.Lock()
l, ok := routes.HTTP.Get(cfg.Link) l, ok := routes.HTTP.Get(cfg.Link)
var linked *ReveseProxyRoute var linked *ReveseProxyRoute
if ok { if ok {
lbLock.Unlock()
linked = l.(*ReveseProxyRoute) linked = l.(*ReveseProxyRoute)
lb = linked.loadBalancer lb = linked.loadBalancer
lb.UpdateConfigIfNeeded(cfg) lb.UpdateConfigIfNeeded(cfg)
@ -184,17 +179,20 @@ func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
_ = lb.Start(parent) // always return nil _ = lb.Start(parent) // always return nil
linked = &ReveseProxyRoute{ linked = &ReveseProxyRoute{
Route: &Route{ Route: &Route{
Alias: cfg.Link, Alias: cfg.Link,
Homepage: r.Homepage, Homepage: r.Homepage,
HealthMon: lb,
}, },
HealthMon: lb,
loadBalancer: lb, loadBalancer: lb,
handler: lb, handler: lb,
} }
routes.HTTP.Add(linked) routes.HTTP.AddKey(cfg.Link, linked)
r.task.OnFinished("entrypoint_remove_route", func() { routes.All.AddKey(cfg.Link, linked)
routes.HTTP.Del(linked) r.task.OnFinished("remove_loadbalancer_route", func() {
routes.HTTP.DelKey(cfg.Link)
routes.All.DelKey(cfg.Link)
}) })
lbLock.Unlock()
} }
r.loadBalancer = lb r.loadBalancer = lb

View file

@ -3,7 +3,9 @@ package route
import ( import (
"context" "context"
"fmt" "fmt"
"runtime"
"strings" "strings"
"sync"
"time" "time"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
@ -14,7 +16,7 @@ import (
"github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/homepage"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
netutils "github.com/yusing/go-proxy/internal/net" netutils "github.com/yusing/go-proxy/internal/net"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/proxmox" "github.com/yusing/go-proxy/internal/proxmox"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
@ -37,7 +39,7 @@ type (
Alias string `json:"alias"` Alias string `json:"alias"`
Scheme route.Scheme `json:"scheme,omitempty"` Scheme route.Scheme `json:"scheme,omitempty"`
Host string `json:"host,omitempty"` Host string `json:"host,omitempty"`
Port route.Port `json:"port,omitempty"` Port route.Port `json:"port"`
Root string `json:"root,omitempty"` Root string `json:"root,omitempty"`
route.HTTPConfig route.HTTPConfig
@ -48,8 +50,10 @@ type (
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"` Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"`
Homepage *homepage.ItemConfig `json:"homepage,omitempty"` Homepage *homepage.ItemConfig `json:"homepage,omitempty"`
AccessLog *accesslog.RequestLoggerConfig `json:"access_log,omitempty"` AccessLog *accesslog.RequestLoggerConfig `json:"access_log,omitempty"`
Agent string `json:"agent,omitempty"`
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"` Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
HealthMon health.HealthMonitor `json:"health,omitempty"`
Metadata `deserialize:"-"` Metadata `deserialize:"-"`
} }
@ -57,15 +61,26 @@ type (
Metadata struct { Metadata struct {
/* Docker only */ /* Docker only */
Container *docker.Container `json:"container,omitempty"` Container *docker.Container `json:"container,omitempty"`
Provider string `json:"provider,omitempty"`
Provider string `json:"provider,omitempty"` // for backward compatibility
// private fields // private fields
LisURL *net.URL `json:"lurl,omitempty"` LisURL *nettypes.URL `json:"lurl,omitempty"`
ProxyURL *net.URL `json:"purl,omitempty"` ProxyURL *nettypes.URL `json:"purl,omitempty"`
Excluded *bool `json:"excluded"`
impl routes.Route
task *task.Task
impl routes.Route
isValidated bool isValidated bool
lastError gperr.Error lastError gperr.Error
provider routes.Provider
agent *agent.AgentConfig
started chan struct{}
once sync.Once
} }
Routes map[string]*Route Routes map[string]*Route
) )
@ -82,8 +97,35 @@ func (r *Route) Validate() gperr.Error {
return r.lastError return r.lastError
} }
r.isValidated = true r.isValidated = true
if r.Agent != "" {
if r.Container != nil {
return gperr.Errorf("specifying agent is not allowed for docker container routes")
}
var ok bool
// by agent address
r.agent, ok = agent.GetAgent(r.Agent)
if !ok {
// fallback to get agent by name
r.agent, ok = agent.GetAgentByName(r.Agent)
if !ok {
return gperr.Errorf("agent %s not found", r.Agent)
}
}
}
r.Finalize() r.Finalize()
r.started = make(chan struct{})
// close the channel when the route is destroyed (if not closed yet).
runtime.AddCleanup(r, func(ch chan struct{}) {
select {
case <-ch:
default:
close(ch)
}
}, r.started)
if r.Idlewatcher != nil && r.Idlewatcher.Proxmox != nil { if r.Idlewatcher != nil && r.Idlewatcher.Proxmox != nil {
node := r.Idlewatcher.Proxmox.Node node := r.Idlewatcher.Proxmox.Node
vmid := r.Idlewatcher.Proxmox.VMID vmid := r.Idlewatcher.Proxmox.VMID
@ -155,13 +197,15 @@ func (r *Route) Validate() gperr.Error {
r.Idlewatcher = r.Container.IdlewatcherConfig r.Idlewatcher = r.Container.IdlewatcherConfig
} }
// return error if route is localhost:<godoxy_port> // return error if route is localhost:<godoxy_port> but route is not agent
switch r.Host { if !r.IsAgent() {
case "localhost", "127.0.0.1": switch r.Host {
switch r.Port.Proxy { case "localhost", "127.0.0.1":
case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort: switch r.Port.Proxy {
if r.Scheme.IsReverseProxy() || r.Scheme == route.SchemeTCP { case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort:
return gperr.Errorf("localhost:%d is reserved for godoxy", r.Port.Proxy) if r.Scheme.IsReverseProxy() || r.Scheme == route.SchemeTCP {
return gperr.Errorf("localhost:%d is reserved for godoxy", r.Port.Proxy)
}
} }
} }
} }
@ -173,17 +217,19 @@ func (r *Route) Validate() gperr.Error {
switch r.Scheme { switch r.Scheme {
case route.SchemeFileServer: case route.SchemeFileServer:
r.ProxyURL = gperr.Collect(errs, net.ParseURL, "file://"+r.Root) r.ProxyURL = gperr.Collect(errs, nettypes.ParseURL, "file://"+r.Root)
r.Host = "" r.Host = ""
r.Port.Proxy = 0 r.Port.Proxy = 0
case route.SchemeHTTP, route.SchemeHTTPS: case route.SchemeHTTP, route.SchemeHTTPS:
if r.Port.Listening != 0 { if r.Port.Listening != 0 {
errs.Addf("unexpected listening port for %s scheme", r.Scheme) errs.Addf("unexpected listening port for %s scheme", r.Scheme)
} }
r.ProxyURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy)) r.ProxyURL = gperr.Collect(errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
case route.SchemeTCP, route.SchemeUDP: case route.SchemeTCP, route.SchemeUDP:
r.LisURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening)) if !r.ShouldExclude() {
r.ProxyURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy)) r.LisURL = gperr.Collect(errs, nettypes.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening))
}
r.ProxyURL = gperr.Collect(errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
} }
if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) { if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) {
@ -212,34 +258,80 @@ func (r *Route) Validate() gperr.Error {
} }
r.impl = impl r.impl = impl
excluded := r.ShouldExclude()
r.Excluded = &excluded
return nil return nil
} }
func (r *Route) Impl() routes.Route {
return r.impl
}
func (r *Route) Task() *task.Task {
return r.task
}
func (r *Route) Start(parent task.Parent) (err gperr.Error) { func (r *Route) Start(parent task.Parent) (err gperr.Error) {
r.once.Do(func() {
err = r.start(parent)
})
return
}
func (r *Route) start(parent task.Parent) gperr.Error {
if r.impl == nil { // should not happen if r.impl == nil { // should not happen
return gperr.New("route not initialized") return gperr.New("route not initialized")
} }
defer close(r.started)
return r.impl.Start(parent) if err := r.impl.Start(parent); err != nil {
return err
}
if conflict, added := routes.All.AddIfNotExists(r.impl); !added {
err := gperr.Errorf("route %s already exists: from %s and %s", r.Alias, r.ProviderName(), conflict.ProviderName())
r.task.FinishAndWait(err)
return err
} else {
// reference here because r.impl will be nil after Finish() is called.
impl := r.impl
r.task.OnCancel("remove_routes_from_all", func() {
routes.All.Del(impl)
})
}
return nil
} }
func (r *Route) Finish(reason any) { func (r *Route) Finish(reason any) {
r.FinishAndWait(reason)
}
func (r *Route) FinishAndWait(reason any) {
if r.impl == nil { if r.impl == nil {
return return
} }
r.impl.Finish(reason) r.task.FinishAndWait(reason)
r.impl = nil r.impl = nil
} }
func (r *Route) Started() bool { func (r *Route) Started() <-chan struct{} {
return r.impl != nil return r.started
}
func (r *Route) GetProvider() routes.Provider {
return r.provider
}
func (r *Route) SetProvider(p routes.Provider) {
r.provider = p
r.Provider = p.ShortName()
} }
func (r *Route) ProviderName() string { func (r *Route) ProviderName() string {
return r.Provider return r.Provider
} }
func (r *Route) TargetURL() *net.URL { func (r *Route) TargetURL() *nettypes.URL {
return r.ProxyURL return r.ProxyURL
} }
@ -260,6 +352,14 @@ func (r *Route) Name() string {
// Key implements pool.Object. // Key implements pool.Object.
func (r *Route) Key() string { func (r *Route) Key() string {
if r.UseLoadBalance() || r.ShouldExclude() {
// for excluded routes and load balanced routes, use provider:alias[-container_id[:8]] as key to make them unique.
if r.Container != nil {
return r.Provider + ":" + r.Alias + "-" + r.Container.ContainerID[:8]
}
return r.Provider + ":" + r.Alias
}
// we need to use alias as key for non-excluded routes because it's being used for subdomain / fqdn lookup for http routes.
return r.Alias return r.Alias
} }
@ -273,19 +373,26 @@ func (r *Route) Type() route.RouteType {
panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias)) panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias))
} }
func (r *Route) Agent() *agent.AgentConfig { func (r *Route) GetAgent() *agent.AgentConfig {
if r.Container == nil { if r.Container != nil && r.Container.Agent != nil {
return nil return r.Container.Agent
} }
return r.Container.Agent return r.agent
} }
func (r *Route) IsAgent() bool { func (r *Route) IsAgent() bool {
return r.Container != nil && r.Container.Agent != nil return r.GetAgent() != nil
} }
func (r *Route) HealthMonitor() health.HealthMonitor { func (r *Route) HealthMonitor() health.HealthMonitor {
return r.impl.HealthMonitor() return r.HealthMon
}
func (r *Route) SetHealthMonitor(m health.HealthMonitor) {
if r.HealthMon != nil && r.HealthMon != m {
r.HealthMon.Finish("health monitor replaced")
}
r.HealthMon = m
} }
func (r *Route) IdlewatcherConfig() *idlewatcher.Config { func (r *Route) IdlewatcherConfig() *idlewatcher.Config {
@ -328,6 +435,12 @@ func (r *Route) IsZeroPort() bool {
} }
func (r *Route) ShouldExclude() bool { func (r *Route) ShouldExclude() bool {
if r.lastError != nil {
return true
}
if r.Excluded != nil {
return *r.Excluded
}
if r.Container != nil { if r.Container != nil {
switch { switch {
case r.Container.IsExcluded: case r.Container.IsExcluded:
@ -342,8 +455,7 @@ func (r *Route) ShouldExclude() bool {
} else if r.IsZeroPort() && r.Scheme != route.SchemeFileServer { } else if r.IsZeroPort() && r.Scheme != route.SchemeFileServer {
return true return true
} }
if strings.HasPrefix(r.Alias, "x-") || if strings.HasSuffix(r.Alias, "-old") {
strings.HasSuffix(r.Alias, "-old") {
return true return true
} }
return false return false
@ -358,6 +470,16 @@ func (r *Route) UseIdleWatcher() bool {
} }
func (r *Route) UseHealthCheck() bool { func (r *Route) UseHealthCheck() bool {
if r.Container != nil {
switch {
case r.Container.Image.Name == "godoxy-agent":
return false
case !r.Container.Running && !r.UseIdleWatcher():
return false
case strings.HasPrefix(r.Container.ContainerName, "buildx_"):
return false
}
}
return !r.HealthCheck.Disable return !r.HealthCheck.Disable
} }
@ -424,7 +546,7 @@ func (r *Route) Finalize() {
if isDocker { if isDocker {
if r.Scheme == "" { if r.Scheme == "" {
for _, p := range cont.PublicPortMapping { for _, p := range cont.PublicPortMapping {
if p.PrivatePort == uint16(pp) && p.Type == "udp" { if int(p.PrivatePort) == pp && p.Type == "udp" {
r.Scheme = "udp" r.Scheme = "udp"
break break
} }
@ -482,6 +604,12 @@ func (r *Route) FinalizeHomepageConfig() {
} }
r.Homepage = r.Homepage.GetOverride(r.Alias) r.Homepage = r.Homepage.GetOverride(r.Alias)
if r.ShouldExclude() && isDocker {
r.Homepage.Show = false
r.Homepage.Name = r.Container.ContainerName // still show container name in metrics page
return
}
hp := r.Homepage hp := r.Homepage
refs := r.References() refs := r.References()
for _, ref := range refs { for _, ref := range refs {

View file

@ -148,3 +148,37 @@ func TestPreferredPort(t *testing.T) {
port := preferredPort(ports) port := preferredPort(ports)
expect.Equal(t, port, 3000) expect.Equal(t, port, 3000)
} }
func TestDockerRouteDisallowAgent(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: route.Port{Proxy: 80},
Agent: "test-agent",
Metadata: Metadata{
Container: &docker.Container{
ContainerID: "test-id",
Image: &docker.ContainerImage{
Name: "test-image",
},
},
},
}
err := r.Validate()
expect.HasError(t, err, "Validate should return error for docker route with agent")
expect.ErrorContains(t, err, "specifying agent is not allowed for docker container routes")
}
func TestRouteAgent(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: route.Port{Proxy: 80},
Agent: "test-agent",
}
err := r.Validate()
expect.NoError(t, err, "Validate should not return error for valid route with agent")
expect.NotNil(t, r.GetAgent(), "GetAgent should return agent")
}

View file

@ -7,13 +7,13 @@ import (
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
"github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/homepage"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
net "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/utils/pool" "github.com/yusing/go-proxy/internal/utils/pool"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types" loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
type ( type (
@ -23,11 +23,13 @@ type (
task.TaskFinisher task.TaskFinisher
pool.Object pool.Object
ProviderName() string ProviderName() string
TargetURL() *net.URL GetProvider() Provider
TargetURL() *nettypes.URL
HealthMonitor() health.HealthMonitor HealthMonitor() health.HealthMonitor
SetHealthMonitor(m health.HealthMonitor)
References() []string References() []string
Started() bool Started() <-chan struct{}
IdlewatcherConfig() *idlewatcher.Config IdlewatcherConfig() *idlewatcher.Config
HealthCheckConfig() *health.HealthCheckConfig HealthCheckConfig() *health.HealthCheckConfig
@ -36,7 +38,7 @@ type (
HomepageItem() *homepage.Item HomepageItem() *homepage.Item
ContainerInfo() *docker.Container ContainerInfo() *docker.Container
Agent() *agent.AgentConfig GetAgent() *agent.AgentConfig
IsDocker() bool IsDocker() bool
IsAgent() bool IsAgent() bool
@ -55,6 +57,13 @@ type (
} }
StreamRoute interface { StreamRoute interface {
Route Route
net.Stream nettypes.Stream
Stream() nettypes.Stream
}
Provider 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
} }
) )

View file

@ -7,15 +7,16 @@ import (
var ( var (
HTTP = pool.New[HTTPRoute]("http_routes") HTTP = pool.New[HTTPRoute]("http_routes")
Stream = pool.New[StreamRoute]("stream_routes") Stream = pool.New[StreamRoute]("stream_routes")
// All is a pool of all routes, including HTTP, Stream routes and also excluded routes.
All = pool.New[Route]("all_routes")
) )
func init() {
All.DisableLog()
}
func Iter(yield func(r Route) bool) { func Iter(yield func(r Route) bool) {
for _, r := range HTTP.Iter { for _, r := range All.Iter {
if !yield(r) {
break
}
}
for _, r := range Stream.Iter {
if !yield(r) { if !yield(r) {
break break
} }
@ -23,12 +24,7 @@ func Iter(yield func(r Route) bool) {
} }
func IterKV(yield func(alias string, r Route) bool) { func IterKV(yield func(alias string, r Route) bool) {
for k, r := range HTTP.Iter { for k, r := range All.Iter {
if !yield(k, r) {
break
}
}
for k, r := range Stream.Iter {
if !yield(k, r) { if !yield(k, r) {
break break
} }
@ -36,12 +32,13 @@ func IterKV(yield func(alias string, r Route) bool) {
} }
func NumRoutes() int { func NumRoutes() int {
return HTTP.Size() + Stream.Size() return All.Size()
} }
func Clear() { func Clear() {
HTTP.Clear() HTTP.Clear()
Stream.Clear() Stream.Clear()
All.Clear()
} }
func GetHTTPRouteOrExact(alias, host string) (HTTPRoute, bool) { func GetHTTPRouteOrExact(alias, host string) (HTTPRoute, bool) {
@ -54,9 +51,5 @@ func GetHTTPRouteOrExact(alias, host string) (HTTPRoute, bool) {
} }
func Get(alias string) (Route, bool) { func Get(alias string) (Route, bool) {
r, ok := HTTP.Get(alias) return All.Get(alias)
if ok {
return r, true
}
return Stream.Get(alias)
} }

View file

@ -9,7 +9,7 @@ import (
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -95,7 +95,7 @@ var commands = map[string]struct {
}, },
validate: validateURL, validate: validateURL,
build: func(args any) CommandHandler { build: func(args any) CommandHandler {
target := args.(*types.URL).String() target := args.(*nettypes.URL).String()
return ReturningCommand(func(w http.ResponseWriter, r *http.Request) { return ReturningCommand(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, target, http.StatusTemporaryRedirect) http.Redirect(w, r, target, http.StatusTemporaryRedirect)
}) })
@ -160,7 +160,7 @@ var commands = map[string]struct {
}, },
validate: validateAbsoluteURL, validate: validateAbsoluteURL,
build: func(args any) CommandHandler { build: func(args any) CommandHandler {
target := args.(*types.URL) target := args.(*nettypes.URL)
if target.Scheme == "" { if target.Scheme == "" {
target.Scheme = "http" target.Scheme = "http"
} }

View file

@ -8,7 +8,7 @@ import (
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -205,7 +205,7 @@ var checkers = map[string]struct {
}, },
validate: validateCIDR, validate: validateCIDR,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
cidr := args.(types.CIDR) cidr := args.(nettypes.CIDR)
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
ip := cached.GetRemoteIP(r) ip := cached.GetRemoteIP(r)
if ip == nil { if ip == nil {

View file

@ -10,7 +10,7 @@ import (
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
type ( type (
@ -62,7 +62,7 @@ func validateURL(args []string) (any, gperr.Error) {
if len(args) != 1 { if len(args) != 1 {
return nil, ErrExpectOneArg return nil, ErrExpectOneArg
} }
u, err := types.ParseURL(args[0]) u, err := nettypes.ParseURL(args[0])
if err != nil { if err != nil {
return nil, ErrInvalidArguments.With(err) return nil, ErrInvalidArguments.With(err)
} }
@ -74,7 +74,7 @@ func validateAbsoluteURL(args []string) (any, gperr.Error) {
if len(args) != 1 { if len(args) != 1 {
return nil, ErrExpectOneArg return nil, ErrExpectOneArg
} }
u, err := types.ParseURL(args[0]) u, err := nettypes.ParseURL(args[0])
if err != nil { if err != nil {
return nil, ErrInvalidArguments.With(err) return nil, ErrInvalidArguments.With(err)
} }
@ -95,7 +95,7 @@ func validateCIDR(args []string) (any, gperr.Error) {
if !strings.Contains(args[0], "/") { if !strings.Contains(args[0], "/") {
args[0] += "/32" args[0] += "/32"
} }
cidr, err := types.ParseCIDR(args[0]) cidr, err := nettypes.ParseCIDR(args[0])
if err != nil { if err != nil {
return nil, ErrInvalidArguments.With(err) return nil, ErrInvalidArguments.With(err)
} }

View file

@ -2,28 +2,24 @@ package route
import ( import (
"context" "context"
"errors" "fmt"
"net"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/idlewatcher" "github.com/yusing/go-proxy/internal/idlewatcher"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/route/stream"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
) )
// TODO: support stream load balance. // TODO: support stream load balance.
type StreamRoute struct { type StreamRoute struct {
*Route *Route
stream nettypes.Stream
net.Stream `json:"-"`
HealthMon health.HealthMonitor `json:"health"`
task *task.Task
l zerolog.Logger l zerolog.Logger
} }
@ -39,95 +35,91 @@ func NewStreamRoute(base *Route) (routes.Route, gperr.Error) {
}, nil }, nil
} }
func (r *StreamRoute) Stream() nettypes.Stream {
return r.stream
}
// Start implements task.TaskStarter. // Start implements task.TaskStarter.
func (r *StreamRoute) Start(parent task.Parent) gperr.Error { func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
if existing, ok := routes.Stream.Get(r.Key()); ok { stream, err := r.initStream()
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName()) if err != nil {
return gperr.Wrap(err)
} }
r.task = parent.Subtask("stream."+r.Name(), true) r.stream = stream
r.Stream = NewStream(r)
r.task = parent.Subtask("stream."+r.Name(), !r.ShouldExclude())
switch { switch {
case r.UseIdleWatcher(): case r.UseIdleWatcher():
waker, err := idlewatcher.NewWatcher(parent, r) waker, err := idlewatcher.NewWatcher(parent, r, r.IdlewatcherConfig())
if err != nil { if err != nil {
r.task.Finish(err) r.task.Finish(err)
return gperr.Wrap(err, "idlewatcher error") return gperr.Wrap(err, "idlewatcher error")
} }
r.Stream = waker r.stream = waker
r.HealthMon = waker r.HealthMon = waker
case r.UseHealthCheck(): case r.UseHealthCheck():
r.HealthMon = monitor.NewMonitor(r) r.HealthMon = monitor.NewMonitor(r)
} }
if err := r.Setup(); err != nil {
r.task.Finish(err)
return gperr.Wrap(err)
}
r.l.Info().Int("port", r.Port.Listening).Msg("listening")
if r.HealthMon != nil { if r.HealthMon != nil {
if err := r.HealthMon.Start(r.task); err != nil { if err := r.HealthMon.Start(r.task); err != nil {
gperr.LogWarn("health monitor error", err, &r.l) gperr.LogWarn("health monitor error", err, &r.l)
} }
} }
go r.acceptConnections() if r.ShouldExclude() {
return nil
}
if err := checkExists(r); err != nil {
return err
}
r.ListenAndServe(r.task.Context(), nil, nil)
r.l = r.l.With().Stringer("rurl", r.ProxyURL).Stringer("laddr", r.LocalAddr()).Logger()
r.l.Info().Msg("stream started")
r.task.OnCancel("close_stream", func() {
r.stream.Close()
r.l.Info().Msg("stream closed")
})
routes.Stream.Add(r) routes.Stream.Add(r)
r.task.OnFinished("entrypoint_remove_route", func() { r.task.OnCancel("remove_route_from_stream", func() {
routes.Stream.Del(r) routes.Stream.Del(r)
}) })
return nil return nil
} }
// Task implements task.TaskStarter. func (r *StreamRoute) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
func (r *StreamRoute) Task() *task.Task { r.stream.ListenAndServe(ctx, preDial, onRead)
return r.task
} }
// Finish implements task.TaskFinisher. func (r *StreamRoute) Close() error {
func (r *StreamRoute) Finish(reason any) { return r.stream.Close()
r.task.Finish(reason)
} }
func (r *StreamRoute) HealthMonitor() health.HealthMonitor { func (r *StreamRoute) LocalAddr() net.Addr {
return r.HealthMon return r.stream.LocalAddr()
} }
func (r *StreamRoute) acceptConnections() { func (r *StreamRoute) initStream() (nettypes.Stream, error) {
defer r.task.Finish("listener closed") lurl, rurl := r.LisURL, r.ProxyURL
if lurl != nil && lurl.Scheme != rurl.Scheme {
go func() { return nil, fmt.Errorf("incoherent scheme is not yet supported: %s != %s", lurl.Scheme, rurl.Scheme)
<-r.task.Context().Done()
r.Close()
}()
for {
select {
case <-r.task.Context().Done():
return
default:
conn, err := r.Accept()
if err != nil {
select {
case <-r.task.Context().Done():
default:
gperr.LogError("accept connection error", err, &r.l)
}
r.task.Finish(err)
return
}
if conn == nil {
panic("connection is nil")
}
go func() {
err := r.Handle(conn)
if err != nil && !errors.Is(err, context.Canceled) {
gperr.LogError("handle connection error", err, &r.l)
}
}()
}
} }
laddr := ":0"
if lurl != nil {
laddr = lurl.Host
}
switch rurl.Scheme {
case "tcp":
return stream.NewTCPTCPStream(laddr, rurl.Host)
case "udp":
return stream.NewUDPUDPStream(laddr, rurl.Host)
}
return nil, fmt.Errorf("unknown scheme: %s", rurl.Scheme)
} }

View file

@ -0,0 +1,12 @@
//go:build debug
package stream
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func logDebugf(stream zerolog.LogObjectMarshaler, format string, v ...any) {
log.Debug().Object("stream", stream).Msgf(format, v...)
}

View file

@ -0,0 +1,7 @@
//go:build !debug
package stream
import "github.com/rs/zerolog"
func logDebugf(stream zerolog.LogObjectMarshaler, format string, v ...any) {}

Some files were not shown because too many files have changed in this diff Show more