mirror of
https://github.com/yusing/godoxy.git
synced 2025-06-14 14:22:33 +02:00
Compare commits
61 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5ce1c7865e | ||
![]() |
7d17a01de1 | ||
![]() |
cabb840a91 | ||
![]() |
4825f768f3 | ||
![]() |
5fdb023188 | ||
![]() |
4abf61a421 | ||
![]() |
96b7c3fcec | ||
![]() |
f8c57d930f | ||
![]() |
880d66c75e | ||
![]() |
4649c8d479 | ||
![]() |
20021b3cae | ||
![]() |
cfa9201f82 | ||
![]() |
b5328fe5e7 | ||
![]() |
25fbcc4ab9 | ||
![]() |
421aaecba4 | ||
![]() |
01773976d1 | ||
![]() |
2263d6063e | ||
![]() |
cfe0f6bb70 | ||
![]() |
a90d2b90d1 | ||
![]() |
af9629424e | ||
![]() |
ee6cf29bc1 | ||
![]() |
c4a780e061 | ||
![]() |
09c244ef3c | ||
![]() |
bd0fe36c53 | ||
![]() |
d240da4393 | ||
![]() |
9470a14fe8 | ||
![]() |
d3568d9c35 | ||
![]() |
44ef351840 | ||
![]() |
a39d527fc1 | ||
![]() |
22ab043e06 | ||
![]() |
b670cdbd49 | ||
![]() |
45e34d691a | ||
![]() |
e82480a639 | ||
![]() |
e39407886d | ||
![]() |
3135e377a9 | ||
![]() |
bdb3343a7c | ||
![]() |
b411c6d504 | ||
![]() |
966a59b5c9 | ||
![]() |
58db228e25 | ||
![]() |
e737737415 | ||
![]() |
9087c4f195 | ||
![]() |
4705989f4b | ||
![]() |
cb506120dd | ||
![]() |
88aaf956e5 | ||
![]() |
ecfd018b0b | ||
![]() |
54bf84dcba | ||
![]() |
57200bc1e9 | ||
![]() |
6f9bb410f5 | ||
![]() |
e62e667b49 | ||
![]() |
abe81541db | ||
![]() |
9e5d33714c | ||
![]() |
93a81fd558 | ||
![]() |
72923b8cfa | ||
![]() |
24ba4c2a46 | ||
![]() |
ed07bf42ce | ||
![]() |
371e756307 | ||
![]() |
32d8292b17 | ||
![]() |
717fd0e58c | ||
![]() |
2628d9e8a8 | ||
![]() |
c90795e614 | ||
![]() |
4a6bed7728 |
123 changed files with 3052 additions and 2078 deletions
|
@ -8,6 +8,8 @@ TZ=ETC/UTC
|
|||
GODOXY_UID=1000
|
||||
GODOXY_GID=1000
|
||||
|
||||
# Set GODOXY_API_JWT_SECURE=false to allow http
|
||||
GODOXY_API_JWT_SECURE=true
|
||||
# API JWT Configuration (common)
|
||||
# generate secret with `openssl rand -base64 32`
|
||||
GODOXY_API_JWT_SECRET=
|
||||
|
|
4
.github/workflows/docker-image.yml
vendored
4
.github/workflows/docker-image.yml
vendored
|
@ -84,10 +84,10 @@ jobs:
|
|||
outputs: type=image,name=${{ env.REGISTRY }}/${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: |
|
||||
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: |
|
||||
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: |
|
||||
VERSION=${{ github.ref_name }}
|
||||
MAKE_ARGS=${{ env.MAKE_ARGS }}
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -30,6 +30,7 @@ todo.md
|
|||
mtrace.json
|
||||
.env
|
||||
.cursorrules
|
||||
.cursor/
|
||||
.windsurfrules
|
||||
test.Dockerfile
|
||||
|
||||
|
@ -37,4 +38,4 @@ node_modules/
|
|||
tsconfig.tsbuildinfo
|
||||
|
||||
!agent.compose.yml
|
||||
!agent/pkg/**
|
||||
!agent/pkg/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Stage 1: deps
|
||||
FROM golang:1.24.3-alpine AS deps
|
||||
FROM golang:1.24.4-alpine AS deps
|
||||
HEALTHCHECK NONE
|
||||
|
||||
# package version does not matter
|
||||
|
|
2
Makefile
2
Makefile
|
@ -31,7 +31,7 @@ endif
|
|||
ifeq ($(debug), 1)
|
||||
CGO_ENABLED = 0
|
||||
GODOXY_DEBUG = 1
|
||||
BUILD_FLAGS += -gcflags=all='-N -l' -tags debug
|
||||
BUILD_FLAGS += -gcflags=all='-N -l' -tags debug -asan
|
||||
else ifeq ($(pprof), 1)
|
||||
CGO_ENABLED = 1
|
||||
GORACE = log_path=logs/pprof strip_path_prefix=$(shell pwd)/ halt_on_error=1
|
||||
|
|
32
agent/go.mod
32
agent/go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/yusing/go-proxy/agent
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
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/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 (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/rs/zerolog v1.34.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/socketproxy v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
@ -33,8 +33,8 @@ require (
|
|||
github.com/diskfs/go-diskfs v1.6.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/docker/cli v28.1.1+incompatible // indirect
|
||||
github.com/docker/docker v28.1.1+incompatible // indirect
|
||||
github.com/docker/cli v28.2.2+incompatible // indirect
|
||||
github.com/docker/docker v28.2.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // 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/validator/v10 v10.26.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/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/gotify/server/v2 v2.6.3 // 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/quic-go/qpack v0.5.1 // 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-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/spf13/afero v1.14.0 // 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/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
68
agent/go.sum
68
agent/go.sum
|
@ -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/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
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.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
|
||||
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/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
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-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-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
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/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
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-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
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.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
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/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/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327 h1:MyHi1+oJ5hVIYpRoQg9YaWPcz0XdlUxuyJix2klbIVo=
|
||||
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 h1:kih+Q38BQKBsbQmv7mZb7OP3YuSPN78ENTZUxCu4T6M=
|
||||
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/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
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/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-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
|
||||
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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/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/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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
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/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
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/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
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.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
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/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
||||
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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
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.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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.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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
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-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=
|
||||
|
@ -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.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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
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-20190911185100-cd5d95a43a6e/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.7.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.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
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-20190412213103-97732733099d/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.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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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.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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
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-20191011141410-1b5146add898/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/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/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
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/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -50,7 +50,7 @@ services:
|
|||
# - 172.16.0.0/12
|
||||
app:
|
||||
image: ghcr.io/yusing/godoxy:${TAG:-latest}
|
||||
container_name: godoxy
|
||||
container_name: godoxy-proxy
|
||||
restart: always
|
||||
network_mode: host # do not change this
|
||||
env_file: .env
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# options:
|
||||
# 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:
|
||||
# default: allow # or deny (default: allow)
|
||||
|
@ -115,7 +115,7 @@ providers:
|
|||
# secret: aaaa-bbbb-cccc-dddd
|
||||
# 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`
|
||||
#
|
||||
# match_domains:
|
||||
|
|
98
go.mod
98
go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/yusing/go-proxy
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
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/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 (
|
||||
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/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/go-acme/lego/v4 v4.23.1 // acme client
|
||||
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/puzpuzpuz/xsync/v4 v4.1.0 // lock free map for concurrent operations
|
||||
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
|
||||
golang.org/x/crypto v0.38.0 // encrypting password with bcrypt
|
||||
golang.org/x/net v0.40.0 // HTTP header utilities
|
||||
golang.org/x/crypto v0.39.0 // encrypting password with bcrypt
|
||||
golang.org/x/net v0.41.0 // HTTP header utilities
|
||||
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 (
|
||||
github.com/docker/cli v28.1.1+incompatible
|
||||
github.com/goccy/go-yaml v1.17.1 // yaml parsing for different config files
|
||||
github.com/docker/cli v28.2.2+incompatible
|
||||
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/luthermonson/go-proxmox v0.2.2
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
|
@ -45,18 +46,18 @@ require (
|
|||
github.com/samber/slog-zerolog/v2 v2.7.3
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/yusing/go-proxy/agent v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/go-proxy/internal/dnsproviders 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-20250612133450-880d66c75e3f
|
||||
github.com/yusing/go-proxy/internal/utils v0.0.0
|
||||
)
|
||||
|
||||
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/compute/metadata v0.7.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/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/resourcemanager/dns/armdns v1.2.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/aliyun/alibaba-cloud-sdk-go v1.63.107 // 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/config v1.29.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.16 // 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.31 // 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.35 // 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/presigned-url v1.12.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // 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.3 // 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.4 // 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.21 // 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/boombuler/barcode v1.0.2 // indirect
|
||||
github.com/buger/goterm v1.0.4 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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-units v0.5.0 // 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/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // 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-ole/go-ole v1.3.0 // 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/gogo/protobuf v1.3.2 // 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/uuid v1.6.0 // 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-retryablehttp v0.7.7 // 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/infobloxopen/infoblox-go-client/v2 v2.10.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/goh v1.0.1 // 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-go v1.6.4 // 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/image-spec v1.1.1 // 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/patrickmn/go-cache v2.1.0+incompatible // 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/sacloud/api-client-go v0.3.0 // 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/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/scaleway/scaleway-sdk-go v1.0.0-beta.33 // 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/sony/gobreaker v1.0.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/viper v1.20.1 // 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/dnspod v1.0.1170 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.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/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/x448/float16 v0.8.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // 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/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric 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/mock v0.5.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.1 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/api v0.234.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/api v0.237.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
@ -243,7 +243,7 @@ require (
|
|||
k8s.io/api v0.33.1 // indirect
|
||||
k8s.io/apimachinery v0.33.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/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
|
|
174
go.sum
174
go.sum
|
@ -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.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
|
||||
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.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
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/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
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/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/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
|
||||
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 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
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/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
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/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.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/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.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
|
||||
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.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
|
||||
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.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||
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.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
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.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
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.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
|
||||
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.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
|
||||
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.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
|
||||
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.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
|
||||
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/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/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/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
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/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE=
|
||||
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/route53 v1.51.1 h1:41HrH51fydStW2Tah74zkqZlJfyx4gXeuGOdsIFuckY=
|
||||
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/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
|
||||
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/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
|
||||
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/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
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.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
|
||||
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.3/go.mod h1:nramkVwrN/StFgVfTZp6Azi1J4lWnq+99fwpJRTasj4=
|
||||
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.52.1/go.mod h1:cpFFGJ0A6WKZjf26TVzYI3qFhbFXXb7xeF5bOOMax6c=
|
||||
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.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
|
||||
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.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
|
||||
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.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.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||
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.228/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
||||
github.com/baidubce/bce-sdk-go v0.9.230 h1:HzELBKiD7QAgYqZ1qHZexoI2A3Lo/6zYGQFvcUbS5cA=
|
||||
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.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
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/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/civo/civogo v0.5.3 h1:FLtiosmNxHErTPonCouosPmg9C+3itmI/qxNxuPQIZs=
|
||||
github.com/civo/civogo v0.5.3/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
|
||||
github.com/civo/civogo v0.6.1 h1:PFOh7rBU0vmj7LTDIv3z7l9uXG4SZyyzScCl3wyTFSc=
|
||||
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
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/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/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
|
||||
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
|
||||
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/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
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.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||
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.18/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco=
|
||||
github.com/exoscale/egoscale/v3 v3.1.20 h1:AZw7VWblEM4+77bjFt9pHBvOQj+spqLkDVxGxK7cbUU=
|
||||
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.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
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.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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
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/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
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.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
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.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
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/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/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/gopsutil/v4 v4.0.0-20250523121925-f87c3159e327 h1:MyHi1+oJ5hVIYpRoQg9YaWPcz0XdlUxuyJix2klbIVo=
|
||||
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 h1:kih+Q38BQKBsbQmv7mZb7OP3YuSPN78ENTZUxCu4T6M=
|
||||
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.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
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-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-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
|
||||
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/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
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.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||
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.150/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 h1:vqrLDi/UZpBEliQXLfdavmtETgGLj82iU8Tja0URzqI=
|
||||
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/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=
|
||||
|
@ -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/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/linode/linodego v1.51.0 h1:8S0vNQRUZAuumGFe8h/huCzbyExxlvOUR2Uf0oDoJ/8=
|
||||
github.com/linode/linodego v1.51.0/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw=
|
||||
github.com/linode/linodego v1.52.1 h1:HJ1cz1n9n3chRP9UrtqmP91+xTi0Q5l+H/4z4tpkwgQ=
|
||||
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.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||
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/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||
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.91.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.93.1 h1:lIvy/6aQOUenQI+cxXH1wDBJeXFPO9Du3CaomXeYFaY=
|
||||
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/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
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/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
|
||||
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.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA=
|
||||
github.com/sacloud/iaas-api-go v1.16.0 h1:JUj7f5yHSSakhy0N8qsZ0a5IYpbUgsv4x5rN8pgRbEg=
|
||||
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/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
|
||||
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/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
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/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
||||
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.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
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.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
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.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
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.1172 h1:cfh0m0esFTjBZ6YZRCVWUDs772KOfxQR/mKY7zL8/qg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/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.1170/go.mod h1:OS8GJ/x+p/6Wchc6Lp6iA9Gf9sQ/it0xatH1AXGX8PY=
|
||||
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.1186 h1:LlFJcouP8DMN4tLWfF9oYG+x4tzd40ISMMZ8MaGNH80=
|
||||
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.1184 h1:nZ6YVgr1X332hEI8bQEyW4Hhp9FFOYEZ6Q8fSxND2pQ=
|
||||
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/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
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/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/volcengine/volc-sdk-golang v1.0.209 h1:aYCVO2fQ4meqLZ+XjVLcKzDn7kYfrUekqp48iioH3ug=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.209/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.211 h1:FgwD+1phyy+un4Qk2YqooYtp6XpvNDQ4a/fpsCAtDHo=
|
||||
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/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
|
||||
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.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.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
|
||||
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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
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.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
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-20180807140117-3d87b88a115f/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.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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
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-20180826012351-8a410e7b638d/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.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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
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-20190226205417-e64efc72b421/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.7.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.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
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-20180830151530-49385e6e1522/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.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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
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-20190308202827-9d24e82272b4/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.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.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
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-20180525024113-a5b4c53f6e8b/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.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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
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-20191011141410-1b5146add898/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.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||
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.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg=
|
||||
google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
|
||||
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.4.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/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/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
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 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
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.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
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.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
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/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=
|
||||
|
@ -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/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
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-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
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.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
|
|
|
@ -5,18 +5,18 @@ import (
|
|||
"time"
|
||||
|
||||
"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/gpwebsocket"
|
||||
"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) {
|
||||
gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error {
|
||||
return conn.WriteJSON(cfg.ListAgents())
|
||||
return conn.WriteJSON(agent.ListAgents())
|
||||
})
|
||||
} else {
|
||||
gphttp.RespondJSON(w, r, cfg.ListAgents())
|
||||
gphttp.RespondJSON(w, r, agent.ListAgents())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ func RenewCert(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
conn, err := gpwebsocket.Initiate(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
|
@ -43,7 +44,7 @@ func getDockerClients() (DockerClients, gperr.Error) {
|
|||
dockerClients[name] = dockerClient
|
||||
}
|
||||
|
||||
for _, agent := range cfg.ListAgents() {
|
||||
for _, agent := range agent.ListAgents() {
|
||||
dockerClient, err := docker.NewClient(agent.FakeDockerHost())
|
||||
if err != nil {
|
||||
connErrs.Add(err)
|
||||
|
@ -65,7 +66,7 @@ func getDockerClient(server string) (*docker.SharedClient, bool, error) {
|
|||
}
|
||||
}
|
||||
if host == "" {
|
||||
for _, agent := range cfg.ListAgents() {
|
||||
for _, agent := range agent.ListAgents() {
|
||||
if agent.Name() == server {
|
||||
host = agent.FakeDockerHost()
|
||||
break
|
||||
|
|
|
@ -44,6 +44,12 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// try with alias
|
||||
GetFavIconFromAlias(w, req, alias)
|
||||
return
|
||||
}
|
||||
|
||||
func GetFavIconFromAlias(w http.ResponseWriter, req *http.Request, alias string) {
|
||||
// try with route.Icon
|
||||
r, ok := routes.HTTP.Get(alias)
|
||||
if !ok {
|
||||
|
|
|
@ -39,7 +39,7 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -4,16 +4,15 @@ import (
|
|||
"net/http"
|
||||
|
||||
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/metrics/systeminfo"
|
||||
"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/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()
|
||||
agentAddr := query.Get("agent_addr")
|
||||
query.Del("agent_addr")
|
||||
|
@ -22,7 +21,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
|
|||
return
|
||||
}
|
||||
|
||||
agent, ok := cfg.GetAgent(agentAddr)
|
||||
agent, ok := agentPkg.GetAgent(agentAddr)
|
||||
if !ok {
|
||||
gphttp.NotFound(w, "agent_addr")
|
||||
return
|
||||
|
@ -41,7 +40,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
gphttp.WriteBody(w, respData)
|
||||
} 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()
|
||||
r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
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) {
|
||||
// check for session token
|
||||
sessionToken, err := r.Cookie(CookieOauthSessionToken)
|
||||
|
@ -182,10 +185,21 @@ func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !rateLimit.Allow() {
|
||||
http.Error(w, "auth rate limit exceeded", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
state := generateState()
|
||||
SetTokenCookie(w, r, CookieOauthState, state, 300*time.Second)
|
||||
// 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) {
|
||||
|
|
|
@ -124,7 +124,8 @@ func (auth *UserPassAuth) PostAuthCallbackHandler(w http.ResponseWriter, r *http
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
|
@ -16,7 +17,15 @@ var (
|
|||
)
|
||||
|
||||
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 {
|
||||
|
|
|
@ -6,31 +6,8 @@ import (
|
|||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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) {
|
||||
if slices.ContainsFunc(cfg.value.Providers.Agents, func(a *agent.AgentConfig) bool {
|
||||
return a.Addr == host
|
||||
|
@ -44,23 +21,17 @@ func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PE
|
|||
if err != nil {
|
||||
return 0, gperr.Wrap(err, "failed to start agent")
|
||||
}
|
||||
addAgent(&agentCfg)
|
||||
agent.AddAgent(&agentCfg)
|
||||
|
||||
provider := provider.NewAgentProvider(&agentCfg)
|
||||
if err := cfg.errIfExists(provider); err != nil {
|
||||
agent.RemoveAgent(&agentCfg)
|
||||
return 0, err
|
||||
}
|
||||
err = provider.LoadRoutes()
|
||||
if err != nil {
|
||||
agent.RemoveAgent(&agentCfg)
|
||||
return 0, gperr.Wrap(err, "failed to load routes")
|
||||
}
|
||||
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
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/api"
|
||||
autocert "github.com/yusing/go-proxy/internal/autocert"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
|
@ -117,13 +118,13 @@ func Reload() gperr.Error {
|
|||
newCfg := newConfig()
|
||||
err := newCfg.load()
|
||||
if err != nil {
|
||||
newCfg.task.Finish(err)
|
||||
newCfg.task.FinishAndWait(err)
|
||||
return gperr.New(ansi.Warning("using last config")).With(err)
|
||||
}
|
||||
|
||||
// cancel all current subtasks -> wait
|
||||
// -> replace config -> start new subtasks
|
||||
config.GetInstance().(*Config).Task().Finish("config changed")
|
||||
config.GetInstance().(*Config).Task().FinishAndWait("config changed")
|
||||
newCfg.Start(StartAllServers)
|
||||
config.SetInstance(newCfg)
|
||||
return nil
|
||||
|
@ -323,14 +324,14 @@ func (cfg *Config) loadRouteProviders(providers *config.Providers) gperr.Error {
|
|||
errs := gperr.NewBuilder("route provider errors")
|
||||
results := gperr.NewBuilder("loaded route providers")
|
||||
|
||||
removeAllAgents()
|
||||
agentPkg.RemoveAllAgents()
|
||||
|
||||
for _, agent := range providers.Agents {
|
||||
if err := agent.Start(cfg.task.Context()); err != nil {
|
||||
errs.Add(gperr.PrependSubject(agent.String(), err))
|
||||
continue
|
||||
}
|
||||
addAgent(agent)
|
||||
agentPkg.AddAgent(agent)
|
||||
p := proxy.NewAgentProvider(agent)
|
||||
if err := cfg.errIfExists(p); err != nil {
|
||||
errs.Add(err.Subject(p.String()))
|
||||
|
|
|
@ -2,36 +2,25 @@ package config
|
|||
|
||||
import (
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
"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 {
|
||||
entries := make(map[string]*provider.Provider)
|
||||
cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
|
||||
entries := make(map[string]*provider.Provider, cfg.providers.Size())
|
||||
for _, p := range cfg.providers.Range {
|
||||
entries[p.ShortName()] = p
|
||||
})
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
func (cfg *Config) RouteProviderList() []config.RouteProviderListResponse {
|
||||
var list []config.RouteProviderListResponse
|
||||
cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
|
||||
list := make([]config.RouteProviderListResponse, 0, cfg.providers.Size())
|
||||
for _, p := range cfg.providers.Range {
|
||||
list = append(list, config.RouteProviderListResponse{
|
||||
ShortName: p.ShortName(),
|
||||
FullName: p.String(),
|
||||
})
|
||||
})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
|
@ -40,13 +29,13 @@ func (cfg *Config) Statistics() map[string]any {
|
|||
var total uint16
|
||||
providerStats := make(map[string]provider.ProviderStats)
|
||||
|
||||
cfg.providers.RangeAll(func(_ string, p *provider.Provider) {
|
||||
for _, p := range cfg.providers.Range {
|
||||
stats := p.Statistics()
|
||||
providerStats[p.ShortName()] = stats
|
||||
rps.AddOther(stats.RPs)
|
||||
streams.AddOther(stats.Streams)
|
||||
total += stats.RPs.Total + stats.Streams.Total
|
||||
})
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"total": total,
|
||||
|
|
|
@ -52,9 +52,7 @@ type (
|
|||
Statistics() map[string]any
|
||||
RouteProviderList() []RouteProviderListResponse
|
||||
Context() context.Context
|
||||
GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool)
|
||||
VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error)
|
||||
ListAgents() []*agent.AgentConfig
|
||||
AutoCertProvider() *autocert.Provider
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module github.com/yusing/go-proxy/internal/dnsproviders
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
replace github.com/yusing/go-proxy => ../..
|
||||
|
||||
|
@ -8,16 +8,16 @@ replace github.com/yusing/go-proxy/internal/utils => ../utils
|
|||
|
||||
require (
|
||||
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 (
|
||||
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/compute/metadata v0.7.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/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/resourcemanager/dns/armdns v1.2.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/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // 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/config v1.29.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.16 // 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.31 // 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.35 // 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/presigned-url v1.12.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // 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.3 // 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.4 // 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.21 // 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/boombuler/barcode v1.0.2 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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/felixge/httpsnoop v1.0.4 // 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/go-errors/errors v1.5.1 // 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-playground/locales v0.14.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-viper/mapstructure/v2 v2.2.1 // 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/gogo/protobuf v1.3.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-retryablehttp v0.7.7 // 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/infobloxopen/infoblox-go-client/v2 v2.10.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/goh v1.0.1 // 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-go v1.6.4 // 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/nzdjb/go-metaname v1.0.0 // 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/patrickmn/go-cache v2.1.0+incompatible // 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/sacloud/api-client-go v0.3.0 // 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/sagikazarmark/locafero v0.9.0 // 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/sourcegraph/conc v0.3.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/viper v1.20.1 // indirect
|
||||
github.com/stretchr/testify v1.10.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/dnspod v1.0.1170 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // 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/x448/float16 v0.8.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // 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/contrib/instrumentation/net/http/otelhttp v0.61.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/multierr v1.11.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.1 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.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/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/api v0.234.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/api v0.237.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
@ -189,7 +189,7 @@ require (
|
|||
k8s.io/api v0.33.1 // indirect
|
||||
k8s.io/apimachinery v0.33.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/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
|
|
|
@ -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.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
|
||||
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.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
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/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
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/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/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
|
||||
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 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
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/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
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/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.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/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.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
|
||||
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.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
|
||||
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.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||
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.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
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.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
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.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
|
||||
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.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
|
||||
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.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
|
||||
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.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
|
||||
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/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/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/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
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/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE=
|
||||
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/route53 v1.51.1 h1:41HrH51fydStW2Tah74zkqZlJfyx4gXeuGOdsIFuckY=
|
||||
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/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
|
||||
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/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
|
||||
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/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
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.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
|
||||
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.3/go.mod h1:nramkVwrN/StFgVfTZp6Azi1J4lWnq+99fwpJRTasj4=
|
||||
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.52.1/go.mod h1:cpFFGJ0A6WKZjf26TVzYI3qFhbFXXb7xeF5bOOMax6c=
|
||||
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.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
|
||||
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.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
|
||||
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.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.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||
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.228/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
||||
github.com/baidubce/bce-sdk-go v0.9.230 h1:HzELBKiD7QAgYqZ1qHZexoI2A3Lo/6zYGQFvcUbS5cA=
|
||||
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.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
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/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/civo/civogo v0.5.3 h1:FLtiosmNxHErTPonCouosPmg9C+3itmI/qxNxuPQIZs=
|
||||
github.com/civo/civogo v0.5.3/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
|
||||
github.com/civo/civogo v0.6.1 h1:PFOh7rBU0vmj7LTDIv3z7l9uXG4SZyyzScCl3wyTFSc=
|
||||
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
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.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||
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.18/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco=
|
||||
github.com/exoscale/egoscale/v3 v3.1.20 h1:AZw7VWblEM4+77bjFt9pHBvOQj+spqLkDVxGxK7cbUU=
|
||||
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.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
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.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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
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/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
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.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
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.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
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/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
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-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-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE=
|
||||
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/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
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.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||
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.150/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 h1:vqrLDi/UZpBEliQXLfdavmtETgGLj82iU8Tja0URzqI=
|
||||
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/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=
|
||||
|
@ -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/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/linode/linodego v1.51.0 h1:8S0vNQRUZAuumGFe8h/huCzbyExxlvOUR2Uf0oDoJ/8=
|
||||
github.com/linode/linodego v1.51.0/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw=
|
||||
github.com/linode/linodego v1.52.1 h1:HJ1cz1n9n3chRP9UrtqmP91+xTi0Q5l+H/4z4tpkwgQ=
|
||||
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.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||
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/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||
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.91.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.93.1 h1:lIvy/6aQOUenQI+cxXH1wDBJeXFPO9Du3CaomXeYFaY=
|
||||
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/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
|
||||
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/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE=
|
||||
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.15.0/go.mod h1:AU6TM3giGEeyl/p1FAYfpwMDpkl7aLco2svdYXnrAfA=
|
||||
github.com/sacloud/iaas-api-go v1.16.0 h1:JUj7f5yHSSakhy0N8qsZ0a5IYpbUgsv4x5rN8pgRbEg=
|
||||
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/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8=
|
||||
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.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
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.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
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.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
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.1172 h1:cfh0m0esFTjBZ6YZRCVWUDs772KOfxQR/mKY7zL8/qg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/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.1170/go.mod h1:OS8GJ/x+p/6Wchc6Lp6iA9Gf9sQ/it0xatH1AXGX8PY=
|
||||
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.1186 h1:LlFJcouP8DMN4tLWfF9oYG+x4tzd40ISMMZ8MaGNH80=
|
||||
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.1184 h1:nZ6YVgr1X332hEI8bQEyW4Hhp9FFOYEZ6Q8fSxND2pQ=
|
||||
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/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
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/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/volcengine/volc-sdk-golang v1.0.209 h1:aYCVO2fQ4meqLZ+XjVLcKzDn7kYfrUekqp48iioH3ug=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.209/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.211 h1:FgwD+1phyy+un4Qk2YqooYtp6XpvNDQ4a/fpsCAtDHo=
|
||||
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/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w=
|
||||
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.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.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
|
||||
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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
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.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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
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-20180807140117-3d87b88a115f/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.8.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.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
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-20180826012351-8a410e7b638d/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.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
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.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
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-20190226205417-e64efc72b421/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-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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
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-20180830151530-49385e6e1522/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.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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
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-20190308202827-9d24e82272b4/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.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.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
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-20180525024113-a5b4c53f6e8b/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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
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.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
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-20191011141410-1b5146add898/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.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||
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.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg=
|
||||
google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
|
||||
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.4.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/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/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
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 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
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.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
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.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
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/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=
|
||||
|
@ -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/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
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-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
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.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
)
|
||||
|
||||
|
@ -58,20 +57,16 @@ func initClientCleaner() {
|
|||
case <-ticker.C:
|
||||
closeTimedOutClients()
|
||||
case <-cleaner.Context().Done():
|
||||
clientMapMu.Lock()
|
||||
for _, c := range clientMap {
|
||||
delete(clientMap, c.Key())
|
||||
c.Client.Close()
|
||||
}
|
||||
clientMapMu.Unlock()
|
||||
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() {
|
||||
|
@ -126,7 +121,7 @@ func NewClient(host string) (*SharedClient, error) {
|
|||
var dial func(ctx context.Context) (net.Conn, error)
|
||||
|
||||
if agent.IsDockerHostAgent(host) {
|
||||
cfg, ok := config.GetInstance().GetAgent(host)
|
||||
cfg, ok := agent.GetAgent(host)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("agent %q not found", host))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package docker
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
@ -9,9 +11,7 @@ import (
|
|||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
||||
"github.com/yusing/go-proxy/internal/serialization"
|
||||
|
@ -35,6 +35,7 @@ type (
|
|||
|
||||
Mounts []string `json:"mounts"`
|
||||
|
||||
Network string `json:"network,omitempty"`
|
||||
PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port
|
||||
PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port
|
||||
PublicHostname string `json:"public_hostname"`
|
||||
|
@ -45,6 +46,8 @@ type (
|
|||
IsExplicit bool `json:"is_explicit"`
|
||||
IsHostNetworkMode bool `json:"is_host_network_mode"`
|
||||
Running bool `json:"running"`
|
||||
|
||||
Errors *containerError `json:"errors"`
|
||||
}
|
||||
ContainerImage struct {
|
||||
Author string `json:"author,omitempty"`
|
||||
|
@ -55,16 +58,24 @@ type (
|
|||
|
||||
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) {
|
||||
isExplicit := false
|
||||
_, isExplicit := c.Labels[LabelAliases]
|
||||
helper := containerHelper{c}
|
||||
for lbl := range c.Labels {
|
||||
if strings.HasPrefix(lbl, NSProxy+".") {
|
||||
isExplicit = true
|
||||
} else {
|
||||
delete(c.Labels, lbl)
|
||||
if !isExplicit {
|
||||
// walk through all labels to check if any label starts with NSProxy.
|
||||
for lbl := range c.Labels {
|
||||
if strings.HasPrefix(lbl, NSProxy+".") {
|
||||
isExplicit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
network := helper.getDeleteLabel(LabelNetwork)
|
||||
|
||||
isExcluded, _ := strconv.ParseBool(helper.getDeleteLabel(LabelExclude))
|
||||
res = &Container{
|
||||
|
@ -77,6 +88,7 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
|
|||
|
||||
Mounts: helper.getMounts(),
|
||||
|
||||
Network: network,
|
||||
PublicPortMapping: helper.getPublicPortMapping(),
|
||||
PrivatePortMapping: helper.getPrivatePortMapping(),
|
||||
|
||||
|
@ -89,15 +101,19 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
|
|||
|
||||
if agent.IsDockerHostAgent(dockerHost) {
|
||||
var ok bool
|
||||
res.Agent, ok = config.GetInstance().GetAgent(dockerHost)
|
||||
res.Agent, ok = agent.GetAgent(dockerHost)
|
||||
if !ok {
|
||||
log.Error().Msgf("agent %q not found", dockerHost)
|
||||
res.addError(fmt.Errorf("agent %q not found", dockerHost))
|
||||
}
|
||||
}
|
||||
|
||||
res.setPrivateHostname(helper)
|
||||
res.setPublicHostname()
|
||||
res.loadDeleteIdlewatcherLabels(helper)
|
||||
|
||||
if res.PrivateHostname == "" && res.PublicHostname == "" && res.Running {
|
||||
res.addError(ErrNoNetwork)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -124,14 +140,30 @@ func (c *Container) UpdatePorts() error {
|
|||
continue
|
||||
}
|
||||
c.PublicPortMapping[portInt] = container.Port{
|
||||
PublicPort: uint16(portInt),
|
||||
PrivatePort: uint16(portInt),
|
||||
PublicPort: uint16(portInt), //nolint:gosec
|
||||
PrivatePort: uint16(portInt), //nolint:gosec
|
||||
Type: proto,
|
||||
}
|
||||
}
|
||||
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/lib/postgresql/data": {},
|
||||
"/var/lib/mysql": {},
|
||||
|
@ -184,7 +216,6 @@ func (c *Container) setPublicHostname() {
|
|||
}
|
||||
url, err := url.Parse(c.DockerHost)
|
||||
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"
|
||||
return
|
||||
}
|
||||
|
@ -198,8 +229,32 @@ func (c *Container) setPrivateHostname(helper containerHelper) {
|
|||
if helper.NetworkSettings == nil {
|
||||
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 != "" {
|
||||
c.Network = k // update network to the first network
|
||||
c.PrivateHostname = v.IPAddress
|
||||
return
|
||||
}
|
||||
|
@ -214,22 +269,34 @@ func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
|
|||
"stop_timeout": helper.getDeleteLabel(LabelStopTimeout),
|
||||
"stop_signal": helper.getDeleteLabel(LabelStopSignal),
|
||||
"start_endpoint": helper.getDeleteLabel(LabelStartEndpoint),
|
||||
"depends_on": c.Dependencies(),
|
||||
}
|
||||
|
||||
// ensure it's deleted from labels
|
||||
helper.getDeleteLabel(LabelDependsOn)
|
||||
|
||||
// set only if idlewatcher is enabled
|
||||
idleTimeout := cfg["idle_timeout"]
|
||||
if idleTimeout != "" {
|
||||
idwCfg := &idlewatcher.Config{
|
||||
Docker: &idlewatcher.DockerConfig{
|
||||
DockerHost: c.DockerHost,
|
||||
ContainerID: c.ContainerID,
|
||||
ContainerName: c.ContainerName,
|
||||
},
|
||||
idwCfg := new(idlewatcher.Config)
|
||||
idwCfg.Docker = &idlewatcher.DockerConfig{
|
||||
DockerHost: c.DockerHost,
|
||||
ContainerID: c.ContainerID,
|
||||
ContainerName: c.ContainerName,
|
||||
}
|
||||
|
||||
err := serialization.MapUnmarshalValidate(cfg, idwCfg)
|
||||
if err != nil {
|
||||
gperr.LogWarn("invalid idlewatcher config", gperr.PrependSubject(c.ContainerName, err))
|
||||
c.addError(err)
|
||||
} else {
|
||||
c.IdlewatcherConfig = idwCfg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Container) addError(err error) {
|
||||
if c.Errors == nil {
|
||||
c.Errors = new(containerError)
|
||||
}
|
||||
c.Errors.Add(err)
|
||||
}
|
||||
|
|
|
@ -48,10 +48,13 @@ func (c containerHelper) parseImage() *ContainerImage {
|
|||
im.Author = strings.Join(slashSep[:len(slashSep)-1], "/")
|
||||
im.Name = slashSep[len(slashSep)-1]
|
||||
} else {
|
||||
im.Author = "library"
|
||||
im.Name = slashSep[0]
|
||||
}
|
||||
if len(colonSep) > 1 {
|
||||
im.Tag = colonSep[1]
|
||||
} else {
|
||||
im.Tag = "latest"
|
||||
}
|
||||
return im
|
||||
}
|
||||
|
|
|
@ -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
34
internal/docker/errors.go
Normal 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()))
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
@ -9,10 +13,12 @@ type LabelMap = map[string]any
|
|||
|
||||
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)
|
||||
errs := gperr.NewBuilder("labels error")
|
||||
|
||||
ExpandWildcard(labels, aliases...)
|
||||
|
||||
for lbl, value := range labels {
|
||||
parts := strutils.SplitRune(lbl, '.')
|
||||
if parts[0] != NSProxy {
|
||||
|
@ -50,3 +56,93 @@ func ParseLabels(labels map[string]string) (LabelMap, gperr.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,63 @@ package docker_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"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) {
|
||||
for range b.N {
|
||||
for b.Loop() {
|
||||
_, _ = docker.ParseLabels(map[string]string{
|
||||
"proxy.a.host": "localhost",
|
||||
"proxy.a.port": "4444",
|
||||
"proxy.a.scheme": "http",
|
||||
"proxy.a.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
"proxy.b.port": "4444",
|
||||
"proxy.*.scheme": "http",
|
||||
"proxy.*.middlewares.request.hide_headers": "X-Header1,X-Header2",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,6 @@ const (
|
|||
LabelStopTimeout = NSProxy + ".stop_timeout"
|
||||
LabelStopSignal = NSProxy + ".stop_signal"
|
||||
LabelStartEndpoint = NSProxy + ".start_endpoint"
|
||||
LabelDependsOn = NSProxy + ".depends_on"
|
||||
LabelNetwork = NSProxy + ".network"
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ package homepage
|
|||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -11,7 +11,7 @@ type route interface {
|
|||
pool.Object
|
||||
ProviderName() string
|
||||
References() []string
|
||||
TargetURL() *gpnet.URL
|
||||
TargetURL() *nettypes.URL
|
||||
}
|
||||
|
||||
type httpRoute interface {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package idlewatcher
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func (w *Watcher) cancelled(reqCtx context.Context) bool {
|
||||
func (w *Watcher) canceled(reqCtx context.Context) bool {
|
||||
select {
|
||||
case <-reqCtx.Done():
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Watcher) waitStarted(reqCtx context.Context) bool {
|
||||
select {
|
||||
case <-reqCtx.Done():
|
||||
return false
|
||||
case <-w.route.Started():
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,3 +39,10 @@ func Watchers() iter.Seq2[string, watcherDebug] {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fmtErr(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
|
65
internal/idlewatcher/errors.go
Normal file
65
internal/idlewatcher/errors.go
Normal 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
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package idlewatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -38,10 +36,6 @@ func (w *Watcher) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
default:
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +44,14 @@ func isFaviconPath(path string) bool {
|
|||
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) {
|
||||
w.resetIdleTimer()
|
||||
|
||||
|
@ -60,8 +62,7 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
|
|||
|
||||
// handle favicon request
|
||||
if isFaviconPath(r.URL.Path) {
|
||||
r.URL.RawQuery = "alias=" + w.rp.TargetName
|
||||
favicon.GetFavIcon(rw, r)
|
||||
favicon.GetFavIconFromAlias(rw, r, w.route.Name())
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -90,32 +91,32 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
|
|||
return false
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeoutCause(r.Context(), w.cfg.WakeTimeout, errors.New("wake timeout"))
|
||||
defer cancel()
|
||||
|
||||
if w.cancelled(ctx) {
|
||||
gphttp.ServerError(rw, r, context.Cause(ctx), http.StatusServiceUnavailable)
|
||||
ctx := r.Context()
|
||||
if w.canceled(ctx) {
|
||||
w.redirectToStartEndpoint(rw, r)
|
||||
return false
|
||||
}
|
||||
|
||||
w.l.Trace().Msg("signal received")
|
||||
err := w.wakeIfStopped()
|
||||
err := w.Wake(ctx)
|
||||
if err != nil {
|
||||
gphttp.ServerError(rw, r, err)
|
||||
return false
|
||||
}
|
||||
|
||||
var ready bool
|
||||
|
||||
for {
|
||||
w.resetIdleTimer()
|
||||
|
||||
if w.cancelled(ctx) {
|
||||
gphttp.ServerError(rw, r, context.Cause(ctx), http.StatusServiceUnavailable)
|
||||
if w.canceled(ctx) {
|
||||
w.redirectToStartEndpoint(rw, r)
|
||||
return false
|
||||
}
|
||||
|
||||
w, ready, err = checkUpdateState(w.Key())
|
||||
if !w.waitStarted(ctx) {
|
||||
return false
|
||||
}
|
||||
|
||||
ready, err := w.checkUpdateState()
|
||||
if err != nil {
|
||||
gphttp.ServerError(rw, r, err)
|
||||
return false
|
||||
|
|
|
@ -2,49 +2,54 @@ package idlewatcher
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
gpnet "github.com/yusing/go-proxy/internal/net/types"
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
)
|
||||
|
||||
// Setup implements types.Stream.
|
||||
func (w *Watcher) Addr() net.Addr {
|
||||
return w.stream.Addr()
|
||||
var _ nettypes.Stream = (*Watcher)(nil)
|
||||
|
||||
// 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.
|
||||
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.
|
||||
// Close implements nettypes.Stream.
|
||||
func (w *Watcher) Close() error {
|
||||
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()
|
||||
|
||||
// pass through if container is already ready
|
||||
|
@ -53,27 +58,27 @@ func (w *Watcher) wakeFromStream() error {
|
|||
}
|
||||
|
||||
w.l.Debug().Msg("wake signal received")
|
||||
err := w.wakeIfStopped()
|
||||
err := w.Wake(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeoutCause(w.task.Context(), w.cfg.WakeTimeout, errors.New("wake timeout"))
|
||||
defer cancel()
|
||||
|
||||
var ready bool
|
||||
|
||||
for {
|
||||
if w.cancelled(ctx) {
|
||||
return context.Cause(ctx)
|
||||
w.resetIdleTimer()
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
if ready {
|
||||
w.resetIdleTimer()
|
||||
w.l.Debug().Stringer("url", w.hc.URL()).Msg("container is ready, passing through")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package idlewatcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
|
@ -80,43 +79,6 @@ func (w *Watcher) Detail() string {
|
|||
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.
|
||||
func (w *Watcher) MarshalJSON() ([]byte, error) {
|
||||
url := w.hc.URL()
|
||||
|
@ -135,3 +97,32 @@ func (w *Watcher) MarshalJSON() ([]byte, error) {
|
|||
Detail: detail,
|
||||
}).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
|
||||
}
|
||||
|
|
|
@ -10,16 +10,26 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
Config struct {
|
||||
ProviderConfig struct {
|
||||
Proxmox *ProxmoxConfig `json:"proxmox,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"`
|
||||
WakeTimeout time.Duration `json:"wake_timeout" json_ext:"duration"`
|
||||
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
|
||||
StartEndpoint string `json:"start_endpoint,omitempty"` // Optional path that must be hit to start container
|
||||
DependsOn []string `json:"depends_on,omitempty"`
|
||||
}
|
||||
StopMethod string
|
||||
Signal string
|
||||
|
@ -55,11 +65,11 @@ func (c *Config) ContainerName() string {
|
|||
if c.Docker != nil {
|
||||
return c.Docker.ContainerName
|
||||
}
|
||||
return "lxc " + strconv.Itoa(c.Proxmox.VMID)
|
||||
return "lxc-" + strconv.Itoa(c.Proxmox.VMID)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
errs := gperr.NewBuilder("idlewatcher config validation error")
|
||||
|
|
|
@ -35,7 +35,8 @@ func TestValidateStartEndpoint(t *testing.T) {
|
|||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cfg := Config{StartEndpoint: tc.input}
|
||||
cfg := new(Config)
|
||||
cfg.StartEndpoint = tc.input
|
||||
err := cfg.validateStartEndpoint()
|
||||
if err == nil {
|
||||
expect.Equal(t, cfg.StartEndpoint, tc.input)
|
||||
|
|
|
@ -3,13 +3,13 @@ package idlewatcher
|
|||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type Waker interface {
|
||||
health.HealthMonitor
|
||||
http.Handler
|
||||
net.Stream
|
||||
nettypes.Stream
|
||||
Wake() error
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package idlewatcher
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"maps"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -12,7 +14,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/idlewatcher/provider"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
||||
"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/task"
|
||||
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/health"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
type (
|
||||
routeHelper struct {
|
||||
route routes.Route
|
||||
rp *reverseproxy.ReverseProxy
|
||||
stream net.Stream
|
||||
stream nettypes.Stream
|
||||
hc health.HealthChecker
|
||||
}
|
||||
|
||||
|
@ -48,8 +53,15 @@ type (
|
|||
state atomic.Value[*containerState]
|
||||
lastReset atomic.Value[time.Time]
|
||||
|
||||
ticker *time.Ticker
|
||||
task *task.Task
|
||||
idleTicker *time.Ticker
|
||||
task *task.Task
|
||||
|
||||
dependsOn []*dependency
|
||||
}
|
||||
|
||||
dependency struct {
|
||||
*Watcher
|
||||
waitHealthy bool
|
||||
}
|
||||
|
||||
StopCallback func() error
|
||||
|
@ -60,6 +72,7 @@ const ContextKey = "idlewatcher.watcher"
|
|||
var (
|
||||
watcherMap = make(map[string]*Watcher)
|
||||
watcherMapMu sync.RWMutex
|
||||
singleFlight singleflight.Group
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -79,70 +92,193 @@ var (
|
|||
|
||||
const reqTimeout = 3 * time.Second
|
||||
|
||||
// prevents dependencies from being stopped automatically.
|
||||
const neverTick = time.Duration(1<<63 - 1)
|
||||
|
||||
// TODO: fix stream type.
|
||||
func NewWatcher(parent task.Parent, r routes.Route) (*Watcher, error) {
|
||||
cfg := r.IdlewatcherConfig()
|
||||
func NewWatcher(parent task.Parent, r routes.Route, cfg *idlewatcher.Config) (*Watcher, error) {
|
||||
key := cfg.Key()
|
||||
|
||||
watcherMapMu.RLock()
|
||||
// if the watcher already exists, finish it
|
||||
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()
|
||||
|
||||
w = &Watcher{
|
||||
ticker: time.NewTicker(cfg.IdleTimeout),
|
||||
cfg: cfg,
|
||||
routeHelper: routeHelper{
|
||||
hc: monitor.NewMonitor(r),
|
||||
},
|
||||
if exists {
|
||||
if len(cfg.DependsOn) > 0 {
|
||||
w.cfg.DependsOn = cfg.DependsOn
|
||||
}
|
||||
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 providerType string
|
||||
var err error
|
||||
var kind string
|
||||
switch {
|
||||
case cfg.Docker != nil:
|
||||
p, err = provider.NewDockerProvider(cfg.Docker.DockerHost, cfg.Docker.ContainerID)
|
||||
providerType = "docker"
|
||||
kind = "docker"
|
||||
default:
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
w.provider = p
|
||||
w.l = log.With().
|
||||
Str("provider", providerType).
|
||||
Str("container", cfg.ContainerName()).
|
||||
Logger()
|
||||
|
||||
switch r := r.(type) {
|
||||
case routes.ReverseProxyRoute:
|
||||
w.rp = r.ReverseProxy()
|
||||
case routes.StreamRoute:
|
||||
w.stream = r
|
||||
w.stream = r.Stream()
|
||||
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)
|
||||
defer cancel()
|
||||
status, err := w.provider.ContainerStatus(ctx)
|
||||
if err != nil {
|
||||
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:
|
||||
shutdownTimeout := max(time.Second, cfg.StopTimeout-idleWakerCheckTimeout)
|
||||
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()
|
||||
defer watcherMapMu.Unlock()
|
||||
watcherMap[key] = w
|
||||
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.idleTicker.Stop()
|
||||
w.provider.Close()
|
||||
w.task.Finish(cause)
|
||||
}()
|
||||
}
|
||||
|
||||
w.ticker.Stop()
|
||||
w.provider.Close()
|
||||
w.task.Finish(cause)
|
||||
}()
|
||||
hcCfg := w.hc.Config()
|
||||
hcCfg.BaseContext = func() context.Context {
|
||||
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 {
|
||||
w.l.Info().Msg("idlewatcher reloaded")
|
||||
w.l.Debug().Msg("idlewatcher reloaded")
|
||||
} else {
|
||||
w.l.Info().Msg("idlewatcher started")
|
||||
}
|
||||
|
@ -185,18 +331,75 @@ func (w *Watcher) Key() string {
|
|||
return w.cfg.Key()
|
||||
}
|
||||
|
||||
func (w *Watcher) Wake() error {
|
||||
return w.wakeIfStopped()
|
||||
// Wake wakes the container.
|
||||
//
|
||||
// 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()
|
||||
if state.status == idlewatcher.ContainerStatusRunning {
|
||||
w.l.Debug().Msg("container is already running")
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(w.task.Context(), w.cfg.WakeTimeout)
|
||||
ctx, cancel := context.WithTimeout(ctx, w.cfg.WakeTimeout)
|
||||
defer cancel()
|
||||
switch state.status {
|
||||
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 {
|
||||
// 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 err := w.stopDependencies(); err != nil {
|
||||
return w.newWatcherError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg := w.cfg
|
||||
ctx, cancel := context.WithTimeout(w.task.Context(), cfg.StopTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cfg.StopTimeout)
|
||||
defer cancel()
|
||||
|
||||
// stop itself first.
|
||||
var err error
|
||||
switch cfg.StopMethod {
|
||||
case idlewatcher.StopMethodPause:
|
||||
return w.provider.ContainerPause(ctx)
|
||||
err = w.provider.ContainerPause(ctx)
|
||||
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:
|
||||
return w.provider.ContainerKill(ctx, cfg.StopSignal)
|
||||
err = w.provider.ContainerKill(ctx, cfg.StopSignal)
|
||||
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() {
|
||||
w.ticker.Reset(w.cfg.IdleTimeout)
|
||||
w.idleTicker.Reset(w.cfg.IdleTimeout)
|
||||
w.lastReset.Store(time.Now())
|
||||
}
|
||||
|
||||
func (w *Watcher) expires() time.Time {
|
||||
if !w.running() {
|
||||
if !w.running() || w.cfg.IdleTimeout <= 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
return w.lastReset.Load().Add(w.cfg.IdleTimeout)
|
||||
|
@ -278,15 +513,15 @@ func (w *Watcher) watchUntilDestroy() (returnCause error) {
|
|||
w.l.Info().Msg("awaken")
|
||||
case e.Action.IsContainerStop(): // stop / kill / die
|
||||
w.setNapping(idlewatcher.ContainerStatusStopped)
|
||||
w.ticker.Stop()
|
||||
w.idleTicker.Stop()
|
||||
case e.Action.IsContainerPause(): // pause
|
||||
w.setNapping(idlewatcher.ContainerStatusPaused)
|
||||
w.ticker.Stop()
|
||||
w.idleTicker.Stop()
|
||||
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:
|
||||
w.ticker.Stop()
|
||||
case <-w.idleTicker.C:
|
||||
w.idleTicker.Stop()
|
||||
if w.running() {
|
||||
err := w.stopByMethod()
|
||||
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)
|
||||
default:
|
||||
w.l.Info().Str("reason", "idle timeout").Msg("container stopped")
|
||||
w.l.Info().Msg("idle timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fmtErr(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
func (w *Watcher) dedupDependencies() {
|
||||
// remove from dependencies if the dependency is also a dependency of another dependency, or have duplicates.
|
||||
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
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -25,7 +25,7 @@ type (
|
|||
}
|
||||
Host string
|
||||
CIDR struct {
|
||||
gpnet.CIDR
|
||||
nettypes.CIDR
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
. "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"
|
||||
expect "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
@ -157,7 +157,7 @@ func TestHeaderFilter(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCIDRFilter(t *testing.T) {
|
||||
cidr := []*CIDR{{gpnet.CIDR{
|
||||
cidr := []*CIDR{{nettypes.CIDR{
|
||||
IP: net.ParseIP("192.168.10.0"),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
}}}
|
||||
|
|
|
@ -73,7 +73,6 @@ func (m *memLogger) Write(p []byte) (n int, err error) {
|
|||
func (m *memLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := gpwebsocket.Initiate(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -34,11 +34,16 @@ type (
|
|||
City = maxmind.City
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
updateInterval = 24 * time.Hour
|
||||
httpClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
updateTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
var httpClient = &http.Client{
|
||||
Timeout: updateTimeout,
|
||||
}
|
||||
|
||||
var (
|
||||
ErrResponseNotOK = gperr.New("response not OK")
|
||||
ErrDownloadFailure = gperr.New("download failure")
|
||||
)
|
||||
|
|
|
@ -166,12 +166,12 @@ func (s *SystemInfo) collectDisksInfo(ctx context.Context, lastResult *SystemInf
|
|||
}
|
||||
s.DisksIO = ioCounters
|
||||
if lastResult != nil {
|
||||
interval := time.Now().Unix() - lastResult.Timestamp
|
||||
interval := since(lastResult.Timestamp)
|
||||
for name, disk := range s.DisksIO {
|
||||
if lastUsage, ok := lastResult.DisksIO[name]; ok {
|
||||
disk.ReadSpeed = float64(disk.ReadBytes-lastUsage.ReadBytes) / 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]
|
||||
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.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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ package gpwebsocket
|
|||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
)
|
||||
|
||||
func warnNoMatchDomains() {
|
||||
|
@ -30,12 +30,17 @@ func SetWebsocketAllowedDomains(h http.Header, domains []string) {
|
|||
h[HeaderXGoDoxyWebsocketAllowedDomains] = domains
|
||||
}
|
||||
|
||||
var localAddresses = []string{"127.0.0.1", "10.0.*.*", "172.16.*.*", "192.168.*.*"}
|
||||
|
||||
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) {
|
||||
upgrader := websocket.Upgrader{}
|
||||
upgrader := websocket.Upgrader{
|
||||
Error: errHandler,
|
||||
}
|
||||
|
||||
allowedDomains := WebsocketAllowedDomains(r.Header)
|
||||
if len(allowedDomains) == 0 {
|
||||
|
@ -49,9 +54,13 @@ func Initiate(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
|
|||
if err != nil {
|
||||
host = r.Host
|
||||
}
|
||||
if slices.Contains(localAddresses, host) {
|
||||
if host == "localhost" || host == agent.AgentHost {
|
||||
return true
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
return ip.IsLoopback() || ip.IsPrivate()
|
||||
}
|
||||
for _, domain := range allowedDomains {
|
||||
if domain[0] == '.' {
|
||||
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) {
|
||||
conn, err := Initiate(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
@ -102,3 +110,15 @@ func WriteText(conn *websocket.Conn, msg string) error {
|
|||
_ = conn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
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"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ type (
|
|||
_ U.NoCopy
|
||||
|
||||
name string
|
||||
url *net.URL
|
||||
url *nettypes.URL
|
||||
weight Weight
|
||||
|
||||
http.Handler `json:"-"`
|
||||
|
@ -26,14 +26,14 @@ type (
|
|||
health.HealthMonitor
|
||||
Name() string
|
||||
Key() string
|
||||
URL() *net.URL
|
||||
URL() *nettypes.URL
|
||||
Weight() Weight
|
||||
SetWeight(weight Weight)
|
||||
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{
|
||||
name: name,
|
||||
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 {
|
||||
srv := &server{
|
||||
weight: Weight(weight),
|
||||
url: net.MustParseURL("http://localhost"),
|
||||
url: nettypes.MustParseURL("http://localhost"),
|
||||
}
|
||||
return srv
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func (srv *server) Name() string {
|
|||
return srv.name
|
||||
}
|
||||
|
||||
func (srv *server) URL() *net.URL {
|
||||
func (srv *server) URL() *nettypes.URL {
|
||||
return srv.url
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/entrypoint"
|
||||
. "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/types"
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
routeTypes "github.com/yusing/go-proxy/internal/route/types"
|
||||
"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) {
|
||||
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{
|
||||
"response": {
|
||||
"bypass": "path /test/* | path /api",
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
"github.com/go-playground/validator/v10"
|
||||
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"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
)
|
||||
|
@ -14,12 +14,11 @@ import (
|
|||
type (
|
||||
cidrWhitelist struct {
|
||||
CIDRWhitelistOpts
|
||||
Tracer
|
||||
cachedAddr F.Map[string, bool] // cache for trusted IPs
|
||||
}
|
||||
CIDRWhitelistOpts struct {
|
||||
Allow []*types.CIDR `validate:"min=1"`
|
||||
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
|
||||
Allow []*nettypes.CIDR `validate:"min=1"`
|
||||
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
|
||||
Message string
|
||||
}
|
||||
)
|
||||
|
@ -27,7 +26,7 @@ type (
|
|||
var (
|
||||
CIDRWhiteList = NewMiddleware[cidrWhitelist]()
|
||||
cidrWhitelistDefaults = CIDRWhitelistOpts{
|
||||
Allow: []*types.CIDR{},
|
||||
Allow: []*nettypes.CIDR{},
|
||||
StatusCode: http.StatusForbidden,
|
||||
Message: "IP not allowed",
|
||||
}
|
||||
|
@ -64,13 +63,11 @@ func (wl *cidrWhitelist) checkIP(w http.ResponseWriter, r *http.Request) bool {
|
|||
if cidr.Contains(ip) {
|
||||
wl.cachedAddr.Store(r.RemoteAddr, true)
|
||||
allow = true
|
||||
wl.AddTracef("client %s is allowed", ipStr).With("allowed CIDR", cidr)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allow {
|
||||
wl.cachedAddr.Store(r.RemoteAddr, false)
|
||||
wl.AddTracef("client %s is forbidden", ipStr).With("allowed CIDRs", wl.Allow)
|
||||
}
|
||||
}
|
||||
if !allow {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/rs/zerolog/log"
|
||||
"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/strutils"
|
||||
)
|
||||
|
@ -33,7 +33,7 @@ var (
|
|||
cfCIDRsMu sync.Mutex
|
||||
|
||||
// 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(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
|
||||
|
@ -45,7 +45,7 @@ var CloudflareRealIP = NewMiddleware[cloudflareRealIP]()
|
|||
|
||||
// setup implements MiddlewareWithSetup.
|
||||
func (cri *cloudflareRealIP) setup() {
|
||||
cri.realIP.RealIPOpts = RealIPOpts{
|
||||
cri.realIP = realIP{
|
||||
Header: "CF-Connecting-IP",
|
||||
Recursive: true,
|
||||
}
|
||||
|
@ -60,15 +60,7 @@ func (cri *cloudflareRealIP) before(w http.ResponseWriter, r *http.Request) bool
|
|||
return cri.realIP.before(w, r)
|
||||
}
|
||||
|
||||
func (cri *cloudflareRealIP) enableTrace() {
|
||||
cri.realIP.enableTrace()
|
||||
}
|
||||
|
||||
func (cri *cloudflareRealIP) getTracer() *Tracer {
|
||||
return cri.realIP.getTracer()
|
||||
}
|
||||
|
||||
func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
|
||||
func tryFetchCFCIDR() (cfCIDRs []*nettypes.CIDR) {
|
||||
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
|
||||
return
|
||||
}
|
||||
|
@ -83,7 +75,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
|
|||
if common.IsTest {
|
||||
cfCIDRs = localCIDRs
|
||||
} else {
|
||||
cfCIDRs = make([]*types.CIDR, 0, 30)
|
||||
cfCIDRs = make([]*nettypes.CIDR, 0, 30)
|
||||
err := errors.Join(
|
||||
fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs),
|
||||
fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs),
|
||||
|
@ -103,7 +95,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
|
|||
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)
|
||||
defer cancel()
|
||||
|
||||
|
@ -132,7 +124,7 @@ func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error {
|
|||
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...)
|
||||
return nil
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
|
@ -52,10 +51,6 @@ type (
|
|||
MiddlewareFinalizerWithError interface {
|
||||
finalize() error
|
||||
}
|
||||
MiddlewareWithTracer interface {
|
||||
enableTrace()
|
||||
getTracer() *Tracer
|
||||
}
|
||||
)
|
||||
|
||||
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() {
|
||||
if setup, ok := m.impl.(MiddlewareWithSetup); ok {
|
||||
setup.setup()
|
||||
|
|
|
@ -3,7 +3,6 @@ package middleware
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"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 {
|
||||
chainMid.modResps = append(chainMid.modResps, mr)
|
||||
}
|
||||
comp.setParent(m)
|
||||
}
|
||||
|
||||
if common.IsTrace {
|
||||
for _, child := range chain {
|
||||
child.enableTrace()
|
||||
}
|
||||
m.enableTrace()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
type (
|
||||
modifyRequest struct {
|
||||
ModifyRequestOpts
|
||||
Tracer
|
||||
}
|
||||
// order: add_prefix -> set_headers -> add_headers -> hide_headers
|
||||
ModifyRequestOpts struct {
|
||||
|
@ -31,11 +30,14 @@ func (mr *ModifyRequestOpts) finalize() {
|
|||
|
||||
// before implements RequestModifier.
|
||||
func (mr *modifyRequest) before(w http.ResponseWriter, r *http.Request) (proceed bool) {
|
||||
mr.AddTraceRequest("before modify request", r)
|
||||
|
||||
mr.addPrefix(r, nil, r.URL.Path)
|
||||
mr.modifyHeaders(r, nil, r.Header)
|
||||
mr.AddTraceRequest("after modify request", r)
|
||||
if len(mr.AddPrefix) != 0 {
|
||||
mr.addPrefix(r, r.URL.Path)
|
||||
}
|
||||
if !mr.needVarSubstitution {
|
||||
mr.modifyHeaders(r, r.Header)
|
||||
} else {
|
||||
mr.modifyHeadersWithVarSubstitution(r, nil, r.Header)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -50,42 +52,40 @@ func (mr *ModifyRequestOpts) checkVarSubstitution() {
|
|||
}
|
||||
}
|
||||
|
||||
func (mr *ModifyRequestOpts) modifyHeaders(req *http.Request, resp *http.Response, headers http.Header) {
|
||||
if !mr.needVarSubstitution {
|
||||
for k, v := range mr.SetHeaders {
|
||||
if req != nil && strings.EqualFold(k, "host") {
|
||||
defer func() {
|
||||
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))
|
||||
func (mr *ModifyRequestOpts) modifyHeaders(req *http.Request, headers http.Header) {
|
||||
for k, v := range mr.SetHeaders {
|
||||
if req != nil && strings.EqualFold(k, "host") {
|
||||
defer func() {
|
||||
req.Host = v
|
||||
}()
|
||||
}
|
||||
headers[k] = []string{v}
|
||||
}
|
||||
for k, v := range mr.AddHeaders {
|
||||
headers[k] = append(headers[k], v)
|
||||
}
|
||||
|
||||
for _, k := range mr.HideHeaders {
|
||||
delete(headers, k)
|
||||
}
|
||||
}
|
||||
|
||||
func (mr *modifyRequest) addPrefix(r *http.Request, _ *http.Response, path string) {
|
||||
if len(mr.AddPrefix) == 0 {
|
||||
return
|
||||
func (mr *ModifyRequestOpts) modifyHeadersWithVarSubstitution(req *http.Request, resp *http.Response, headers http.Header) {
|
||||
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))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"slices"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -51,8 +51,8 @@ func TestModifyRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("request_headers", func(t *testing.T) {
|
||||
reqURL := types.MustParseURL("https://my.app/?arg_1=b")
|
||||
upstreamURL := types.MustParseURL("http://test.example.com")
|
||||
reqURL := nettypes.MustParseURL("https://my.app/?arg_1=b")
|
||||
upstreamURL := nettypes.MustParseURL("http://test.example.com")
|
||||
result, err := newMiddlewareTest(ModifyRequest, &testArgs{
|
||||
middlewareOpt: opts,
|
||||
reqURL: reqURL,
|
||||
|
@ -128,8 +128,8 @@ func TestModifyRequest(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reqURL := types.MustParseURL("https://my.app" + tt.path)
|
||||
upstreamURL := types.MustParseURL(tt.upstreamURL)
|
||||
reqURL := nettypes.MustParseURL("https://my.app" + tt.path)
|
||||
upstreamURL := nettypes.MustParseURL(tt.upstreamURL)
|
||||
|
||||
opts["add_prefix"] = tt.addPrefix
|
||||
result, err := newMiddlewareTest(ModifyRequest, &testArgs{
|
||||
|
|
|
@ -6,15 +6,16 @@ import (
|
|||
|
||||
type modifyResponse struct {
|
||||
ModifyRequestOpts
|
||||
Tracer
|
||||
}
|
||||
|
||||
var ModifyResponse = NewMiddleware[modifyResponse]()
|
||||
|
||||
// modifyResponse implements ResponseModifier.
|
||||
func (mr *modifyResponse) modifyResponse(resp *http.Response) error {
|
||||
mr.AddTraceResponse("before modify response", resp)
|
||||
mr.modifyHeaders(resp.Request, resp, resp.Header)
|
||||
mr.AddTraceResponse("after modify response", resp)
|
||||
if !mr.needVarSubstitution {
|
||||
mr.modifyHeaders(resp.Request, resp.Header)
|
||||
} else {
|
||||
mr.modifyHeadersWithVarSubstitution(resp.Request, resp, resp.Header)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"slices"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -54,8 +54,8 @@ func TestModifyResponse(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("response_headers", func(t *testing.T) {
|
||||
reqURL := types.MustParseURL("https://my.app/?arg_1=b")
|
||||
upstreamURL := types.MustParseURL("http://test.example.com")
|
||||
reqURL := nettypes.MustParseURL("https://my.app/?arg_1=b")
|
||||
upstreamURL := nettypes.MustParseURL("http://test.example.com")
|
||||
result, err := newMiddlewareTest(ModifyResponse, &testArgs{
|
||||
middlewareOpt: opts,
|
||||
reqURL: reqURL,
|
||||
|
|
|
@ -13,7 +13,6 @@ type (
|
|||
requestMap = map[string]*rate.Limiter
|
||||
rateLimiter struct {
|
||||
RateLimiterOpts
|
||||
Tracer
|
||||
|
||||
requestMap requestMap
|
||||
mu sync.Mutex
|
||||
|
@ -51,7 +50,6 @@ func (rl *rateLimiter) newLimiter() *rate.Limiter {
|
|||
func (rl *rateLimiter) limit(w http.ResponseWriter, r *http.Request) bool {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
rl.AddTracef("unable to parse remote address %s", r.RemoteAddr)
|
||||
http.Error(w, "Internal error", http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -5,21 +5,18 @@ import (
|
|||
"net/http"
|
||||
|
||||
"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
|
||||
|
||||
type (
|
||||
realIP struct {
|
||||
RealIPOpts
|
||||
Tracer
|
||||
}
|
||||
realIP RealIPOpts
|
||||
RealIPOpts struct {
|
||||
// Header is the name of the header to use for the real client IP
|
||||
Header string `validate:"required"`
|
||||
// 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,
|
||||
the original client address that matches one of the trusted addresses is replaced by
|
||||
|
@ -36,13 +33,13 @@ var (
|
|||
RealIP = NewMiddleware[realIP]()
|
||||
realIPOptsDefault = RealIPOpts{
|
||||
Header: "X-Real-IP",
|
||||
From: []*types.CIDR{},
|
||||
From: []*nettypes.CIDR{},
|
||||
}
|
||||
)
|
||||
|
||||
// setup implements MiddlewareWithSetup.
|
||||
func (ri *realIP) setup() {
|
||||
ri.RealIPOpts = realIPOptsDefault
|
||||
*ri = realIP(realIPOptsDefault)
|
||||
}
|
||||
|
||||
// before implements RequestModifier.
|
||||
|
@ -77,7 +74,6 @@ func (ri *realIP) setRealIP(req *http.Request) {
|
|||
}
|
||||
}
|
||||
if !isTrusted {
|
||||
ri.AddTracef("client ip %s is not trusted", clientIP).With("allowed CIDRs", ri.From)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -90,7 +86,6 @@ func (ri *realIP) setRealIP(req *http.Request) {
|
|||
}
|
||||
|
||||
if len(realIPs) == 0 {
|
||||
ri.AddTracef("no real ip found in header %s", ri.Header).WithRequest(req)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,12 +100,10 @@ func (ri *realIP) setRealIP(req *http.Request) {
|
|||
}
|
||||
|
||||
if lastNonTrustedIP == "" {
|
||||
ri.AddTracef("no non-trusted ip found").With("allowed CIDRs", ri.From).With("ips", realIPs)
|
||||
return
|
||||
}
|
||||
|
||||
req.RemoteAddr = lastNonTrustedIP
|
||||
req.Header.Set(ri.Header, lastNonTrustedIP)
|
||||
req.Header.Set(httpheaders.HeaderXRealIP, lastNonTrustedIP)
|
||||
ri.AddTracef("set real ip %s", lastNonTrustedIP)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -23,7 +23,7 @@ func TestSetRealIPOpts(t *testing.T) {
|
|||
}
|
||||
optExpected := &RealIPOpts{
|
||||
Header: httpheaders.HeaderXRealIP,
|
||||
From: []*types.CIDR{
|
||||
From: []*nettypes.CIDR{
|
||||
{
|
||||
IP: net.ParseIP("127.0.0.0"),
|
||||
Mask: net.IPv4Mask(255, 0, 0, 0),
|
||||
|
@ -71,7 +71,6 @@ func TestSetRealIP(t *testing.T) {
|
|||
|
||||
result, err := newMiddlewareTest(mid, nil)
|
||||
ExpectNoError(t, err)
|
||||
t.Log(traces)
|
||||
ExpectEqual(t, result.ResponseStatus, http.StatusOK)
|
||||
ExpectEqual(t, strings.Split(result.RemoteAddr, ":")[0], testRealIP)
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ import (
|
|||
"net/http"
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestRedirectToHTTPs(t *testing.T) {
|
||||
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
|
||||
reqURL: types.MustParseURL("http://example.com"),
|
||||
reqURL: nettypes.MustParseURL("http://example.com"),
|
||||
})
|
||||
ExpectNoError(t, err)
|
||||
ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect)
|
||||
|
@ -19,7 +19,7 @@ func TestRedirectToHTTPs(t *testing.T) {
|
|||
|
||||
func TestNoRedirect(t *testing.T) {
|
||||
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
|
||||
reqURL: types.MustParseURL("https://example.com"),
|
||||
reqURL: nettypes.MustParseURL("https://example.com"),
|
||||
})
|
||||
ExpectNoError(t, err)
|
||||
ExpectEqual(t, result.ResponseStatus, http.StatusOK)
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -80,11 +80,11 @@ type TestResult struct {
|
|||
|
||||
type testArgs struct {
|
||||
middlewareOpt OptionsRaw
|
||||
upstreamURL *types.URL
|
||||
upstreamURL *nettypes.URL
|
||||
|
||||
realRoundTrip bool
|
||||
|
||||
reqURL *types.URL
|
||||
reqURL *nettypes.URL
|
||||
reqMethod string
|
||||
headers http.Header
|
||||
body []byte
|
||||
|
@ -96,13 +96,13 @@ type testArgs struct {
|
|||
|
||||
func (args *testArgs) setDefaults() {
|
||||
if args.reqURL == nil {
|
||||
args.reqURL = Must(types.ParseURL("https://example.com"))
|
||||
args.reqURL = Must(nettypes.ParseURL("https://example.com"))
|
||||
}
|
||||
if args.reqMethod == "" {
|
||||
args.reqMethod = http.MethodGet
|
||||
}
|
||||
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 {
|
||||
args.respHeaders = http.Header{}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/logging/accesslog"
|
||||
"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"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
@ -93,7 +93,7 @@ type ReverseProxy struct {
|
|||
HandlerFunc http.HandlerFunc
|
||||
|
||||
TargetName string
|
||||
TargetURL *types.URL
|
||||
TargetURL *nettypes.URL
|
||||
}
|
||||
|
||||
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
|
||||
// target's path is "/base" and the incoming request was for "/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 {
|
||||
panic("nil transport")
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ func (p *ReverseProxy) handler(rw http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
req.Header.Del("Forwarded")
|
||||
outreq.Header.Del("Forwarded")
|
||||
httpheaders.RemoveHopByHopHeaders(outreq.Header)
|
||||
|
||||
// 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
|
||||
if req.TLS != nil {
|
||||
if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
|
||||
reqScheme = "https"
|
||||
} else {
|
||||
reqScheme = "http"
|
||||
|
|
|
@ -3,6 +3,7 @@ package server
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -10,7 +11,7 @@ import (
|
|||
|
||||
func convertError(err error) error {
|
||||
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
|
||||
default:
|
||||
return err
|
||||
|
|
|
@ -3,6 +3,8 @@ package server
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -105,7 +107,7 @@ func (s *Server) Start(parent task.Parent) {
|
|||
}
|
||||
Start(subtask, h3, s.acl, &s.l)
|
||||
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.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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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")
|
||||
return
|
||||
}
|
||||
port = l.Addr().(*net.TCPAddr).Port
|
||||
if srv.TLSConfig != nil {
|
||||
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)
|
||||
}
|
||||
serveFunc = getServeFunc(l, srv.Serve)
|
||||
task.OnCancel("stop", func() {
|
||||
stop(srv, l, logger)
|
||||
})
|
||||
case *http3.Server:
|
||||
l, err := lc.ListenPacket(task.Context(), "udp", srv.Addr)
|
||||
if err != nil {
|
||||
HandleError(logger, err, "failed to listen on port")
|
||||
return
|
||||
}
|
||||
port = l.LocalAddr().(*net.UDPAddr).Port
|
||||
if acl != nil {
|
||||
l = acl.WrapUDP(l)
|
||||
}
|
||||
serveFunc = getServeFunc(l, srv.Serve)
|
||||
task.OnCancel("stop", func() {
|
||||
stop(srv, l, logger)
|
||||
})
|
||||
}
|
||||
task.OnCancel("stop", func() {
|
||||
stop(srv, logger)
|
||||
})
|
||||
logStarted(srv, logger)
|
||||
go func() {
|
||||
err := convertError(serveFunc())
|
||||
if err != nil {
|
||||
HandleError(logger, err, "failed to serve "+proto+" server")
|
||||
HandleError(logger, err, "failed to serve "+proto+" server")
|
||||
}
|
||||
task.Finish(err)
|
||||
}()
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -180,7 +187,7 @@ func stop[Server httpServer](srv Server, logger *zerolog.Logger) {
|
|||
ctx, cancel := context.WithTimeout(task.RootContext(), 1*time.Second)
|
||||
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")
|
||||
} else {
|
||||
logger.Info().Str("proto", proto).Str("addr", addr(srv)).Msg("server stopped")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package types
|
||||
package nettypes
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
|
|
@ -1,32 +1,14 @@
|
|||
package types
|
||||
package nettypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type (
|
||||
Stream interface {
|
||||
fmt.Stringer
|
||||
StreamListener
|
||||
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}
|
||||
type Stream interface {
|
||||
ListenAndServe(ctx context.Context, preDial, onRead HookFunc)
|
||||
LocalAddr() net.Addr
|
||||
Close() error
|
||||
}
|
||||
|
||||
func (l NetListenerWrapper) Accept() (StreamConn, error) {
|
||||
return l.Listener.Accept()
|
||||
}
|
||||
type HookFunc func(ctx context.Context) error
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package types
|
||||
package nettypes
|
||||
|
||||
import (
|
||||
urlPkg "net/url"
|
||||
|
|
|
@ -86,7 +86,7 @@ func (f FieldsBody) Format(format *LogFormat) ([]byte, error) {
|
|||
case LogFormatRawJSON:
|
||||
return json.Marshal(f)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown format: %v", format)
|
||||
return f.Format(LogFormatMarkdown)
|
||||
}
|
||||
|
||||
func (l ListBody) Format(format *LogFormat) ([]byte, error) {
|
||||
|
@ -104,7 +104,7 @@ func (l ListBody) Format(format *LogFormat) ([]byte, error) {
|
|||
case LogFormatRawJSON:
|
||||
return json.Marshal(l)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown format: %v", format)
|
||||
return l.Format(LogFormatMarkdown)
|
||||
}
|
||||
|
||||
func (m MessageBody) Format(format *LogFormat) ([]byte, error) {
|
||||
|
@ -114,13 +114,13 @@ func (m MessageBody) Format(format *LogFormat) ([]byte, error) {
|
|||
case LogFormatRawJSON:
|
||||
return json.Marshal(m)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown format: %v", format)
|
||||
return m.Format(LogFormatMarkdown)
|
||||
}
|
||||
|
||||
func (e ErrorBody) Format(format *LogFormat) ([]byte, error) {
|
||||
switch format {
|
||||
case LogFormatRawJSON:
|
||||
return json.Marshal(e)
|
||||
return json.Marshal(e.Error)
|
||||
case LogFormatPlain:
|
||||
return gperr.Plain(e.Error), nil
|
||||
case LogFormatMarkdown:
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package notif
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
)
|
||||
|
@ -14,7 +16,7 @@ type (
|
|||
task *task.Task
|
||||
providers F.Set[Provider]
|
||||
logCh chan *LogMessage
|
||||
retryCh chan *RetryMessage
|
||||
retryMsg F.Set[*RetryMessage]
|
||||
retryTicker *time.Ticker
|
||||
}
|
||||
LogMessage struct {
|
||||
|
@ -23,32 +25,22 @@ type (
|
|||
Body LogBody
|
||||
Color Color
|
||||
}
|
||||
RetryMessage struct {
|
||||
Message *LogMessage
|
||||
Trials int
|
||||
Provider Provider
|
||||
}
|
||||
)
|
||||
|
||||
var dispatcher *Dispatcher
|
||||
|
||||
const retryInterval = 5 * time.Second
|
||||
|
||||
var maxRetries = map[zerolog.Level]int{
|
||||
zerolog.DebugLevel: 1,
|
||||
zerolog.InfoLevel: 1,
|
||||
zerolog.WarnLevel: 3,
|
||||
zerolog.ErrorLevel: 5,
|
||||
zerolog.FatalLevel: 10,
|
||||
zerolog.PanicLevel: 10,
|
||||
}
|
||||
const (
|
||||
retryInterval = time.Second
|
||||
maxBackoffDelay = 5 * time.Minute
|
||||
backoffMultiplier = 2.0
|
||||
)
|
||||
|
||||
func StartNotifDispatcher(parent task.Parent) *Dispatcher {
|
||||
dispatcher = &Dispatcher{
|
||||
task: parent.Subtask("notification", true),
|
||||
providers: F.NewSet[Provider](),
|
||||
logCh: make(chan *LogMessage),
|
||||
retryCh: make(chan *RetryMessage, 100),
|
||||
logCh: make(chan *LogMessage, 100),
|
||||
retryMsg: F.NewSet[*RetryMessage](),
|
||||
retryTicker: time.NewTicker(retryInterval),
|
||||
}
|
||||
go dispatcher.start()
|
||||
|
@ -73,11 +65,10 @@ func (disp *Dispatcher) RegisterProvider(cfg *NotificationConfig) {
|
|||
|
||||
func (disp *Dispatcher) start() {
|
||||
defer func() {
|
||||
dispatcher = nil
|
||||
disp.providers.Clear()
|
||||
close(disp.logCh)
|
||||
close(disp.retryCh)
|
||||
disp.task.Finish(nil)
|
||||
dispatcher = nil
|
||||
}()
|
||||
|
||||
for {
|
||||
|
@ -90,22 +81,7 @@ func (disp *Dispatcher) start() {
|
|||
}
|
||||
go disp.dispatch(msg)
|
||||
case <-disp.retryTicker.C:
|
||||
if len(disp.retryCh) == 0 {
|
||||
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)
|
||||
}
|
||||
disp.processRetries()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,34 +90,100 @@ func (disp *Dispatcher) dispatch(msg *LogMessage) {
|
|||
task := disp.task.Subtask("dispatcher", true)
|
||||
defer task.Finish("notif dispatched")
|
||||
|
||||
l := log.With().
|
||||
Str("level", msg.Level.String()).
|
||||
Str("title", msg.Title).Logger()
|
||||
|
||||
disp.providers.RangeAllParallel(func(p Provider) {
|
||||
if err := msg.notify(task.Context(), p); err != nil {
|
||||
disp.retryCh <- &RetryMessage{
|
||||
Message: msg,
|
||||
Trials: 0,
|
||||
Provider: p,
|
||||
msg := &RetryMessage{
|
||||
Message: msg,
|
||||
Trials: 0,
|
||||
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)
|
||||
defer task.Finish("notif retried")
|
||||
|
||||
errs := gperr.NewBuilder("notification failure")
|
||||
successCount := 0
|
||||
failureCount := 0
|
||||
|
||||
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)
|
||||
if err == nil {
|
||||
msg.NextRetry = time.Time{}
|
||||
successCount++
|
||||
log.Debug().EmbedObject(msg).Msg("notification retry succeeded")
|
||||
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++
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/serialization"
|
||||
)
|
||||
|
@ -72,13 +71,6 @@ func (msg *LogMessage) notify(ctx context.Context, provider Provider) error {
|
|||
|
||||
switch resp.StatusCode {
|
||||
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
|
||||
default:
|
||||
return fmt.Errorf("http status %d: %w", resp.StatusCode, provider.fmtError(resp.Body))
|
||||
|
|
33
internal/notif/retry_message.go
Normal file
33
internal/notif/retry_message.go
Normal 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
26
internal/route/common.go
Normal 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
|
||||
}
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
|
@ -19,9 +18,6 @@ type (
|
|||
FileServer struct {
|
||||
*Route
|
||||
|
||||
Health *monitor.FileServerHealthMonitor `json:"health"`
|
||||
|
||||
task *task.Task
|
||||
middleware *middleware.Middleware
|
||||
handler http.Handler
|
||||
accessLogger *accesslog.AccessLogger
|
||||
|
@ -90,28 +86,27 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
|
|||
}
|
||||
|
||||
if s.UseHealthCheck() {
|
||||
s.Health = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
|
||||
if err := s.Health.Start(s.task); err != nil {
|
||||
s.HealthMon = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
|
||||
if err := s.HealthMon.Start(s.task); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if s.ShouldExclude() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := checkExists(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
routes.HTTP.Add(s)
|
||||
s.task.OnCancel("entrypoint_remove_route", func() {
|
||||
s.task.OnFinished("remove_route_from_http", func() {
|
||||
routes.HTTP.Del(s)
|
||||
})
|
||||
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.
|
||||
func (s *FileServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileServer) HealthMonitor() health.HealthMonitor {
|
||||
return s.Health
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ var (
|
|||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
"postgres": 5432,
|
||||
"pgvecto-rs": 5432,
|
||||
"pgvector": 5432,
|
||||
"rabbitmq": 5672,
|
||||
"redis": 6379,
|
||||
"memcached": 11211,
|
||||
|
|
|
@ -71,7 +71,9 @@ func (p *DockerProvider) loadRoutesImpl() (route.Routes, gperr.Error) {
|
|||
|
||||
for _, c := range containers {
|
||||
container := docker.FromDocker(&c, p.dockerHost)
|
||||
if container.IsExcluded {
|
||||
|
||||
if container.Errors != nil {
|
||||
errs.Add(container.Errors)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -89,10 +91,15 @@ func (p *DockerProvider) loadRoutesImpl() (route.Routes, gperr.Error) {
|
|||
}
|
||||
for k, v := range newEntries {
|
||||
if conflict, ok := routes[k]; ok {
|
||||
errs.Add(gperr.Multiline().
|
||||
err := gperr.Multiline().
|
||||
Addf("route with alias %s already exists", k).
|
||||
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 {
|
||||
routes[k] = v
|
||||
}
|
||||
|
@ -122,11 +129,9 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
|
|||
|
||||
errs := gperr.NewBuilder("label errors")
|
||||
|
||||
m, err := docker.ParseLabels(container.Labels)
|
||||
m, err := docker.ParseLabels(container.Labels, container.Aliases...)
|
||||
errs.Add(err)
|
||||
|
||||
var wildcardProps docker.LabelMap
|
||||
|
||||
for alias, entryMapAny := range m {
|
||||
if len(alias) == 0 {
|
||||
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
|
||||
switch alias[0] {
|
||||
case aliasRefPrefix, aliasRefPrefixAlt:
|
||||
|
@ -187,14 +187,6 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
|
|||
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()
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
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},
|
||||
},
|
||||
}
|
||||
|
@ -372,7 +371,7 @@ func TestImplicitExcludeDatabase(t *testing.T) {
|
|||
t.Run("exposed port detection", func(t *testing.T) {
|
||||
r, ok := makeRoutes(&container.SummaryTrimmed{
|
||||
Names: dummyNames,
|
||||
Ports: []types.Port{
|
||||
Ports: []container.Port{
|
||||
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
|
||||
},
|
||||
})["a"]
|
||||
|
|
|
@ -3,7 +3,7 @@ package provider
|
|||
import (
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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/watcher"
|
||||
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) {
|
||||
oldRoutes := handler.provider.routes
|
||||
oldRoutes := handler.provider.lockCloneRoutes()
|
||||
|
||||
isForceReload := false
|
||||
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 {
|
||||
switch handler.provider.GetType() {
|
||||
case types.ProviderTypeDocker, types.ProviderTypeAgent:
|
||||
case provider.ProviderTypeDocker, provider.ProviderTypeAgent:
|
||||
return route.Container.ContainerID == event.ActorID ||
|
||||
route.Container.ContainerName == event.ActorName
|
||||
case types.ProviderTypeFile:
|
||||
case provider.ProviderTypeFile:
|
||||
return true
|
||||
}
|
||||
// should never happen
|
||||
|
@ -86,12 +86,11 @@ func (handler *EventHandler) Add(parent task.Parent, route *route.Route) {
|
|||
}
|
||||
|
||||
func (handler *EventHandler) Remove(route *route.Route) {
|
||||
route.Finish("route removed")
|
||||
delete(handler.provider.routes, route.Alias)
|
||||
route.FinishAndWait("route removed")
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
handler.errs.Add(err.Subject("update"))
|
||||
|
|
|
@ -33,8 +33,17 @@ func FileProviderImpl(filename string) (ProviderImpl, error) {
|
|||
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) {
|
||||
err = serialization.UnmarshalValidateYAML(data, &routes)
|
||||
err = serialization.UnmarshalValidateYAMLIntercept(data, &routes, removeXPrefix)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,17 @@ package provider
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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"
|
||||
W "github.com/yusing/go-proxy/internal/watcher"
|
||||
"github.com/yusing/go-proxy/internal/watcher/events"
|
||||
|
@ -20,8 +23,9 @@ type (
|
|||
Provider struct {
|
||||
ProviderImpl
|
||||
|
||||
t types.ProviderType
|
||||
routes route.Routes
|
||||
t provider.Type
|
||||
routes route.Routes
|
||||
routesMu sync.RWMutex
|
||||
|
||||
watcher W.Watcher
|
||||
}
|
||||
|
@ -41,7 +45,9 @@ const (
|
|||
|
||||
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}
|
||||
}
|
||||
|
||||
|
@ -50,7 +56,7 @@ func NewFileProvider(filename string) (p *Provider, err error) {
|
|||
if name == "" {
|
||||
return nil, ErrEmptyProviderName
|
||||
}
|
||||
p = newProvider(types.ProviderTypeFile)
|
||||
p = newProvider(provider.ProviderTypeFile)
|
||||
p.ProviderImpl, err = FileProviderImpl(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -60,14 +66,14 @@ func NewFileProvider(filename string) (p *Provider, err error) {
|
|||
}
|
||||
|
||||
func NewDockerProvider(name string, dockerHost string) *Provider {
|
||||
p := newProvider(types.ProviderTypeDocker)
|
||||
p := newProvider(provider.ProviderTypeDocker)
|
||||
p.ProviderImpl = DockerProviderImpl(name, dockerHost)
|
||||
p.watcher = p.NewWatcher()
|
||||
return p
|
||||
}
|
||||
|
||||
func NewAgentProvider(cfg *agent.AgentConfig) *Provider {
|
||||
p := newProvider(types.ProviderTypeAgent)
|
||||
p := newProvider(provider.ProviderTypeAgent)
|
||||
agent := &AgentProvider{
|
||||
AgentConfig: cfg,
|
||||
docker: DockerProviderImpl(cfg.Name(), cfg.FakeDockerHost()),
|
||||
|
@ -77,7 +83,7 @@ func NewAgentProvider(cfg *agent.AgentConfig) *Provider {
|
|||
return p
|
||||
}
|
||||
|
||||
func (p *Provider) GetType() types.ProviderType {
|
||||
func (p *Provider) GetType() provider.Type {
|
||||
return p.t
|
||||
}
|
||||
|
||||
|
@ -86,25 +92,29 @@ func (p *Provider) MarshalText() ([]byte, error) {
|
|||
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.
|
||||
func (p *Provider) Start(parent task.Parent) gperr.Error {
|
||||
errs := gperr.NewBuilder("routes error")
|
||||
errs.EnableConcurrency()
|
||||
|
||||
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 {
|
||||
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(
|
||||
t.Subtask("event_queue", false),
|
||||
providerEventFlushInterval,
|
||||
|
@ -126,15 +136,52 @@ func (p *Provider) Start(parent task.Parent) gperr.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) RangeRoutes(do func(string, *route.Route)) {
|
||||
for alias, r := range p.routes {
|
||||
do(alias, r)
|
||||
func (p *Provider) LoadRoutes() (err gperr.Error) {
|
||||
p.routes, err = p.loadRoutes()
|
||||
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) {
|
||||
r, ok = p.routes[alias]
|
||||
return
|
||||
func (p *Provider) FindService(project, service string) (routes.Route, bool) {
|
||||
switch p.GetType() {
|
||||
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) {
|
||||
|
@ -148,26 +195,51 @@ func (p *Provider) loadRoutes() (routes route.Routes, err gperr.Error) {
|
|||
// set alias and provider, then validate
|
||||
for alias, r := range routes {
|
||||
r.Alias = alias
|
||||
r.Provider = p.ShortName()
|
||||
r.SetProvider(p)
|
||||
if err := r.Validate(); err != nil {
|
||||
errs.Add(err.Subject(alias))
|
||||
delete(routes, alias)
|
||||
continue
|
||||
}
|
||||
if r.ShouldExclude() {
|
||||
delete(routes, alias)
|
||||
continue
|
||||
}
|
||||
r.FinalizeHomepageConfig()
|
||||
}
|
||||
return routes, errs.Error()
|
||||
}
|
||||
|
||||
func (p *Provider) LoadRoutes() (err gperr.Error) {
|
||||
p.routes, err = p.loadRoutes()
|
||||
return
|
||||
func (p *Provider) startRoute(parent task.Parent, r *route.Route) gperr.Error {
|
||||
err := r.Start(parent)
|
||||
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 {
|
||||
return len(p.routes)
|
||||
func (p *Provider) lockAddRoute(r *route.Route) {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package provider
|
|||
|
||||
import (
|
||||
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"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
@ -17,10 +17,10 @@ type (
|
|||
NumUnknown uint16 `json:"unknown"`
|
||||
}
|
||||
ProviderStats struct {
|
||||
Total uint16 `json:"total"`
|
||||
RPs RouteStats `json:"reverse_proxies"`
|
||||
Streams RouteStats `json:"streams"`
|
||||
Type types.ProviderType `json:"type"`
|
||||
Total uint16 `json:"total"`
|
||||
RPs RouteStats `json:"reverse_proxies"`
|
||||
Streams RouteStats `json:"streams"`
|
||||
Type provider.Type `json:"type"`
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package types
|
||||
package provider
|
||||
|
||||
type ProviderType string
|
||||
type Type string
|
||||
|
||||
const (
|
||||
ProviderTypeDocker ProviderType = "docker"
|
||||
ProviderTypeFile ProviderType = "file"
|
||||
ProviderTypeAgent ProviderType = "agent"
|
||||
ProviderTypeDocker Type = "docker"
|
||||
ProviderTypeFile Type = "file"
|
||||
ProviderTypeAgent Type = "agent"
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ package route
|
|||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||
|
@ -15,23 +16,18 @@ import (
|
|||
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/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/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
type ReveseProxyRoute struct {
|
||||
*Route
|
||||
|
||||
HealthMon health.HealthMonitor `json:"health,omitempty"`
|
||||
|
||||
loadBalancer *loadbalancer.LoadBalancer
|
||||
handler http.Handler
|
||||
rp *reverseproxy.ReverseProxy
|
||||
|
||||
task *task.Task
|
||||
}
|
||||
|
||||
var _ routes.ReverseProxyRoute = (*ReveseProxyRoute)(nil)
|
||||
|
@ -43,18 +39,21 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
|
|||
proxyURL := base.ProxyURL
|
||||
|
||||
var trans *http.Transport
|
||||
a := base.Agent()
|
||||
a := base.GetAgent()
|
||||
if a != nil {
|
||||
trans = a.Transport()
|
||||
proxyURL = types.NewURL(agent.HTTPProxyURL)
|
||||
proxyURL = nettypes.NewURL(agent.HTTPProxyURL)
|
||||
} else {
|
||||
trans = gphttp.NewTransport()
|
||||
if httpConfig.NoTLSVerify {
|
||||
trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec
|
||||
}
|
||||
if httpConfig.ResponseHeaderTimeout > 0 {
|
||||
trans.ResponseHeaderTimeout = httpConfig.ResponseHeaderTimeout
|
||||
}
|
||||
if httpConfig.DisableCompression {
|
||||
trans.DisableCompression = true
|
||||
}
|
||||
}
|
||||
|
||||
service := base.Name()
|
||||
|
@ -95,14 +94,11 @@ func (r *ReveseProxyRoute) ReverseProxy() *reverseproxy.ReverseProxy {
|
|||
|
||||
// Start implements task.TaskStarter.
|
||||
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)
|
||||
|
||||
switch {
|
||||
case r.UseIdleWatcher():
|
||||
waker, err := idlewatcher.NewWatcher(parent, r)
|
||||
waker, err := idlewatcher.NewWatcher(parent, r, r.IdlewatcherConfig())
|
||||
if err != nil {
|
||||
r.task.Finish(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() {
|
||||
r.addToLoadBalancer(parent)
|
||||
} else {
|
||||
routes.HTTP.Add(r)
|
||||
r.task.OnFinished("entrypoint_remove_route", func() {
|
||||
r.task.OnCancel("remove_route_from_http", func() {
|
||||
routes.HTTP.Del(r)
|
||||
})
|
||||
}
|
||||
|
@ -149,30 +153,21 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
|
|||
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) {
|
||||
r.handler.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func (r *ReveseProxyRoute) HealthMonitor() health.HealthMonitor {
|
||||
return r.HealthMon
|
||||
}
|
||||
var lbLock sync.Mutex
|
||||
|
||||
func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
|
||||
var lb *loadbalancer.LoadBalancer
|
||||
cfg := r.LoadBalance
|
||||
lbLock.Lock()
|
||||
|
||||
l, ok := routes.HTTP.Get(cfg.Link)
|
||||
var linked *ReveseProxyRoute
|
||||
if ok {
|
||||
lbLock.Unlock()
|
||||
linked = l.(*ReveseProxyRoute)
|
||||
lb = linked.loadBalancer
|
||||
lb.UpdateConfigIfNeeded(cfg)
|
||||
|
@ -184,17 +179,20 @@ func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
|
|||
_ = lb.Start(parent) // always return nil
|
||||
linked = &ReveseProxyRoute{
|
||||
Route: &Route{
|
||||
Alias: cfg.Link,
|
||||
Homepage: r.Homepage,
|
||||
Alias: cfg.Link,
|
||||
Homepage: r.Homepage,
|
||||
HealthMon: lb,
|
||||
},
|
||||
HealthMon: lb,
|
||||
loadBalancer: lb,
|
||||
handler: lb,
|
||||
}
|
||||
routes.HTTP.Add(linked)
|
||||
r.task.OnFinished("entrypoint_remove_route", func() {
|
||||
routes.HTTP.Del(linked)
|
||||
routes.HTTP.AddKey(cfg.Link, linked)
|
||||
routes.All.AddKey(cfg.Link, linked)
|
||||
r.task.OnFinished("remove_loadbalancer_route", func() {
|
||||
routes.HTTP.DelKey(cfg.Link)
|
||||
routes.All.DelKey(cfg.Link)
|
||||
})
|
||||
lbLock.Unlock()
|
||||
}
|
||||
r.loadBalancer = lb
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@ package route
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
@ -14,7 +16,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
||||
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/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
|
@ -37,7 +39,7 @@ type (
|
|||
Alias string `json:"alias"`
|
||||
Scheme route.Scheme `json:"scheme,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Port route.Port `json:"port,omitempty"`
|
||||
Port route.Port `json:"port"`
|
||||
Root string `json:"root,omitempty"`
|
||||
|
||||
route.HTTPConfig
|
||||
|
@ -48,8 +50,10 @@ type (
|
|||
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"`
|
||||
Homepage *homepage.ItemConfig `json:"homepage,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:"-"`
|
||||
}
|
||||
|
@ -57,15 +61,26 @@ type (
|
|||
Metadata struct {
|
||||
/* Docker only */
|
||||
Container *docker.Container `json:"container,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
|
||||
Provider string `json:"provider,omitempty"` // for backward compatibility
|
||||
|
||||
// private fields
|
||||
LisURL *net.URL `json:"lurl,omitempty"`
|
||||
ProxyURL *net.URL `json:"purl,omitempty"`
|
||||
LisURL *nettypes.URL `json:"lurl,omitempty"`
|
||||
ProxyURL *nettypes.URL `json:"purl,omitempty"`
|
||||
|
||||
Excluded *bool `json:"excluded"`
|
||||
|
||||
impl routes.Route
|
||||
task *task.Task
|
||||
|
||||
impl routes.Route
|
||||
isValidated bool
|
||||
lastError gperr.Error
|
||||
provider routes.Provider
|
||||
|
||||
agent *agent.AgentConfig
|
||||
|
||||
started chan struct{}
|
||||
once sync.Once
|
||||
}
|
||||
Routes map[string]*Route
|
||||
)
|
||||
|
@ -82,8 +97,35 @@ func (r *Route) Validate() gperr.Error {
|
|||
return r.lastError
|
||||
}
|
||||
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.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 {
|
||||
node := r.Idlewatcher.Proxmox.Node
|
||||
vmid := r.Idlewatcher.Proxmox.VMID
|
||||
|
@ -155,13 +197,15 @@ func (r *Route) Validate() gperr.Error {
|
|||
r.Idlewatcher = r.Container.IdlewatcherConfig
|
||||
}
|
||||
|
||||
// return error if route is localhost:<godoxy_port>
|
||||
switch r.Host {
|
||||
case "localhost", "127.0.0.1":
|
||||
switch r.Port.Proxy {
|
||||
case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort:
|
||||
if r.Scheme.IsReverseProxy() || r.Scheme == route.SchemeTCP {
|
||||
return gperr.Errorf("localhost:%d is reserved for godoxy", r.Port.Proxy)
|
||||
// return error if route is localhost:<godoxy_port> but route is not agent
|
||||
if !r.IsAgent() {
|
||||
switch r.Host {
|
||||
case "localhost", "127.0.0.1":
|
||||
switch r.Port.Proxy {
|
||||
case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort:
|
||||
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 {
|
||||
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.Port.Proxy = 0
|
||||
case route.SchemeHTTP, route.SchemeHTTPS:
|
||||
if r.Port.Listening != 0 {
|
||||
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:
|
||||
r.LisURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening))
|
||||
r.ProxyURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
||||
if !r.ShouldExclude() {
|
||||
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()) {
|
||||
|
@ -212,34 +258,80 @@ func (r *Route) Validate() gperr.Error {
|
|||
}
|
||||
|
||||
r.impl = impl
|
||||
excluded := r.ShouldExclude()
|
||||
r.Excluded = &excluded
|
||||
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) {
|
||||
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
|
||||
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) {
|
||||
r.FinishAndWait(reason)
|
||||
}
|
||||
|
||||
func (r *Route) FinishAndWait(reason any) {
|
||||
if r.impl == nil {
|
||||
return
|
||||
}
|
||||
r.impl.Finish(reason)
|
||||
r.task.FinishAndWait(reason)
|
||||
r.impl = nil
|
||||
}
|
||||
|
||||
func (r *Route) Started() bool {
|
||||
return r.impl != nil
|
||||
func (r *Route) Started() <-chan struct{} {
|
||||
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 {
|
||||
return r.Provider
|
||||
}
|
||||
|
||||
func (r *Route) TargetURL() *net.URL {
|
||||
func (r *Route) TargetURL() *nettypes.URL {
|
||||
return r.ProxyURL
|
||||
}
|
||||
|
||||
|
@ -260,6 +352,14 @@ func (r *Route) Name() string {
|
|||
|
||||
// Key implements pool.Object.
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -273,19 +373,26 @@ func (r *Route) Type() route.RouteType {
|
|||
panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias))
|
||||
}
|
||||
|
||||
func (r *Route) Agent() *agent.AgentConfig {
|
||||
if r.Container == nil {
|
||||
return nil
|
||||
func (r *Route) GetAgent() *agent.AgentConfig {
|
||||
if r.Container != nil && r.Container.Agent != nil {
|
||||
return r.Container.Agent
|
||||
}
|
||||
return r.Container.Agent
|
||||
return r.agent
|
||||
}
|
||||
|
||||
func (r *Route) IsAgent() bool {
|
||||
return r.Container != nil && r.Container.Agent != nil
|
||||
return r.GetAgent() != nil
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -328,6 +435,12 @@ func (r *Route) IsZeroPort() bool {
|
|||
}
|
||||
|
||||
func (r *Route) ShouldExclude() bool {
|
||||
if r.lastError != nil {
|
||||
return true
|
||||
}
|
||||
if r.Excluded != nil {
|
||||
return *r.Excluded
|
||||
}
|
||||
if r.Container != nil {
|
||||
switch {
|
||||
case r.Container.IsExcluded:
|
||||
|
@ -342,8 +455,7 @@ func (r *Route) ShouldExclude() bool {
|
|||
} else if r.IsZeroPort() && r.Scheme != route.SchemeFileServer {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(r.Alias, "x-") ||
|
||||
strings.HasSuffix(r.Alias, "-old") {
|
||||
if strings.HasSuffix(r.Alias, "-old") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -358,6 +470,16 @@ func (r *Route) UseIdleWatcher() 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
|
||||
}
|
||||
|
||||
|
@ -424,7 +546,7 @@ func (r *Route) Finalize() {
|
|||
if isDocker {
|
||||
if r.Scheme == "" {
|
||||
for _, p := range cont.PublicPortMapping {
|
||||
if p.PrivatePort == uint16(pp) && p.Type == "udp" {
|
||||
if int(p.PrivatePort) == pp && p.Type == "udp" {
|
||||
r.Scheme = "udp"
|
||||
break
|
||||
}
|
||||
|
@ -482,6 +604,12 @@ func (r *Route) FinalizeHomepageConfig() {
|
|||
}
|
||||
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
|
||||
refs := r.References()
|
||||
for _, ref := range refs {
|
||||
|
|
|
@ -148,3 +148,37 @@ func TestPreferredPort(t *testing.T) {
|
|||
port := preferredPort(ports)
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
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/utils/pool"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -23,11 +23,13 @@ type (
|
|||
task.TaskFinisher
|
||||
pool.Object
|
||||
ProviderName() string
|
||||
TargetURL() *net.URL
|
||||
GetProvider() Provider
|
||||
TargetURL() *nettypes.URL
|
||||
HealthMonitor() health.HealthMonitor
|
||||
SetHealthMonitor(m health.HealthMonitor)
|
||||
References() []string
|
||||
|
||||
Started() bool
|
||||
Started() <-chan struct{}
|
||||
|
||||
IdlewatcherConfig() *idlewatcher.Config
|
||||
HealthCheckConfig() *health.HealthCheckConfig
|
||||
|
@ -36,7 +38,7 @@ type (
|
|||
HomepageItem() *homepage.Item
|
||||
ContainerInfo() *docker.Container
|
||||
|
||||
Agent() *agent.AgentConfig
|
||||
GetAgent() *agent.AgentConfig
|
||||
|
||||
IsDocker() bool
|
||||
IsAgent() bool
|
||||
|
@ -55,6 +57,13 @@ type (
|
|||
}
|
||||
StreamRoute interface {
|
||||
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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -7,15 +7,16 @@ import (
|
|||
var (
|
||||
HTTP = pool.New[HTTPRoute]("http_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) {
|
||||
for _, r := range HTTP.Iter {
|
||||
if !yield(r) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, r := range Stream.Iter {
|
||||
for _, r := range All.Iter {
|
||||
if !yield(r) {
|
||||
break
|
||||
}
|
||||
|
@ -23,12 +24,7 @@ func Iter(yield func(r Route) bool) {
|
|||
}
|
||||
|
||||
func IterKV(yield func(alias string, r Route) bool) {
|
||||
for k, r := range HTTP.Iter {
|
||||
if !yield(k, r) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for k, r := range Stream.Iter {
|
||||
for k, r := range All.Iter {
|
||||
if !yield(k, r) {
|
||||
break
|
||||
}
|
||||
|
@ -36,12 +32,13 @@ func IterKV(yield func(alias string, r Route) bool) {
|
|||
}
|
||||
|
||||
func NumRoutes() int {
|
||||
return HTTP.Size() + Stream.Size()
|
||||
return All.Size()
|
||||
}
|
||||
|
||||
func Clear() {
|
||||
HTTP.Clear()
|
||||
Stream.Clear()
|
||||
All.Clear()
|
||||
}
|
||||
|
||||
func GetHTTPRouteOrExact(alias, host string) (HTTPRoute, bool) {
|
||||
|
@ -54,9 +51,5 @@ func GetHTTPRouteOrExact(alias, host string) (HTTPRoute, bool) {
|
|||
}
|
||||
|
||||
func Get(alias string) (Route, bool) {
|
||||
r, ok := HTTP.Get(alias)
|
||||
if ok {
|
||||
return r, true
|
||||
}
|
||||
return Stream.Get(alias)
|
||||
return All.Get(alias)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
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/types"
|
||||
nettypes "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
|
@ -95,7 +95,7 @@ var commands = map[string]struct {
|
|||
},
|
||||
validate: validateURL,
|
||||
build: func(args any) CommandHandler {
|
||||
target := args.(*types.URL).String()
|
||||
target := args.(*nettypes.URL).String()
|
||||
return ReturningCommand(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
@ -160,7 +160,7 @@ var commands = map[string]struct {
|
|||
},
|
||||
validate: validateAbsoluteURL,
|
||||
build: func(args any) CommandHandler {
|
||||
target := args.(*types.URL)
|
||||
target := args.(*nettypes.URL)
|
||||
if target.Scheme == "" {
|
||||
target.Scheme = "http"
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/gobwas/glob"
|
||||
"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/utils/strutils"
|
||||
)
|
||||
|
@ -205,7 +205,7 @@ var checkers = map[string]struct {
|
|||
},
|
||||
validate: validateCIDR,
|
||||
builder: func(args any) CheckFunc {
|
||||
cidr := args.(types.CIDR)
|
||||
cidr := args.(nettypes.CIDR)
|
||||
return func(cached Cache, r *http.Request) bool {
|
||||
ip := cached.GetRemoteIP(r)
|
||||
if ip == nil {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/gobwas/glob"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
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 (
|
||||
|
@ -62,7 +62,7 @@ func validateURL(args []string) (any, gperr.Error) {
|
|||
if len(args) != 1 {
|
||||
return nil, ErrExpectOneArg
|
||||
}
|
||||
u, err := types.ParseURL(args[0])
|
||||
u, err := nettypes.ParseURL(args[0])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidArguments.With(err)
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func validateAbsoluteURL(args []string) (any, gperr.Error) {
|
|||
if len(args) != 1 {
|
||||
return nil, ErrExpectOneArg
|
||||
}
|
||||
u, err := types.ParseURL(args[0])
|
||||
u, err := nettypes.ParseURL(args[0])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidArguments.With(err)
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func validateCIDR(args []string) (any, gperr.Error) {
|
|||
if !strings.Contains(args[0], "/") {
|
||||
args[0] += "/32"
|
||||
}
|
||||
cidr, err := types.ParseCIDR(args[0])
|
||||
cidr, err := nettypes.ParseCIDR(args[0])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidArguments.With(err)
|
||||
}
|
||||
|
|
|
@ -2,28 +2,24 @@ package route
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"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/stream"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
// TODO: support stream load balance.
|
||||
type StreamRoute struct {
|
||||
*Route
|
||||
|
||||
net.Stream `json:"-"`
|
||||
|
||||
HealthMon health.HealthMonitor `json:"health"`
|
||||
|
||||
task *task.Task
|
||||
stream nettypes.Stream
|
||||
|
||||
l zerolog.Logger
|
||||
}
|
||||
|
@ -39,95 +35,91 @@ func NewStreamRoute(base *Route) (routes.Route, gperr.Error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (r *StreamRoute) Stream() nettypes.Stream {
|
||||
return r.stream
|
||||
}
|
||||
|
||||
// Start implements task.TaskStarter.
|
||||
func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
|
||||
if existing, ok := routes.Stream.Get(r.Key()); ok {
|
||||
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
|
||||
stream, err := r.initStream()
|
||||
if err != nil {
|
||||
return gperr.Wrap(err)
|
||||
}
|
||||
r.task = parent.Subtask("stream."+r.Name(), true)
|
||||
r.Stream = NewStream(r)
|
||||
r.stream = stream
|
||||
|
||||
r.task = parent.Subtask("stream."+r.Name(), !r.ShouldExclude())
|
||||
|
||||
switch {
|
||||
case r.UseIdleWatcher():
|
||||
waker, err := idlewatcher.NewWatcher(parent, r)
|
||||
waker, err := idlewatcher.NewWatcher(parent, r, r.IdlewatcherConfig())
|
||||
if err != nil {
|
||||
r.task.Finish(err)
|
||||
return gperr.Wrap(err, "idlewatcher error")
|
||||
}
|
||||
r.Stream = waker
|
||||
r.stream = waker
|
||||
r.HealthMon = waker
|
||||
case r.UseHealthCheck():
|
||||
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 err := r.HealthMon.Start(r.task); err != nil {
|
||||
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)
|
||||
r.task.OnFinished("entrypoint_remove_route", func() {
|
||||
r.task.OnCancel("remove_route_from_stream", func() {
|
||||
routes.Stream.Del(r)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Task implements task.TaskStarter.
|
||||
func (r *StreamRoute) Task() *task.Task {
|
||||
return r.task
|
||||
func (r *StreamRoute) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
|
||||
r.stream.ListenAndServe(ctx, preDial, onRead)
|
||||
}
|
||||
|
||||
// Finish implements task.TaskFinisher.
|
||||
func (r *StreamRoute) Finish(reason any) {
|
||||
r.task.Finish(reason)
|
||||
func (r *StreamRoute) Close() error {
|
||||
return r.stream.Close()
|
||||
}
|
||||
|
||||
func (r *StreamRoute) HealthMonitor() health.HealthMonitor {
|
||||
return r.HealthMon
|
||||
func (r *StreamRoute) LocalAddr() net.Addr {
|
||||
return r.stream.LocalAddr()
|
||||
}
|
||||
|
||||
func (r *StreamRoute) acceptConnections() {
|
||||
defer r.task.Finish("listener closed")
|
||||
|
||||
go func() {
|
||||
<-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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
func (r *StreamRoute) initStream() (nettypes.Stream, error) {
|
||||
lurl, rurl := r.LisURL, r.ProxyURL
|
||||
if lurl != nil && lurl.Scheme != rurl.Scheme {
|
||||
return nil, fmt.Errorf("incoherent scheme is not yet supported: %s != %s", lurl.Scheme, rurl.Scheme)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
12
internal/route/stream/debug_debug.go
Normal file
12
internal/route/stream/debug_debug.go
Normal 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...)
|
||||
}
|
7
internal/route/stream/debug_prod.go
Normal file
7
internal/route/stream/debug_prod.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build !debug
|
||||
|
||||
package stream
|
||||
|
||||
import "github.com/rs/zerolog"
|
||||
|
||||
func logDebugf(stream zerolog.LogObjectMarshaler, format string, v ...any) {}
|
41
internal/route/stream/errors.go
Normal file
41
internal/route/stream/errors.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package stream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"syscall"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func convertErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
switch {
|
||||
case errors.Is(err, context.Canceled),
|
||||
errors.Is(err, io.ErrClosedPipe),
|
||||
errors.Is(err, syscall.ECONNRESET):
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func logErr(stream zerolog.LogObjectMarshaler, err error, msg string) {
|
||||
err = convertErr(err)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.Err(err).Object("stream", stream).Msg(msg)
|
||||
}
|
||||
|
||||
func logErrf(stream zerolog.LogObjectMarshaler, err error, format string, v ...any) {
|
||||
err = convertErr(err)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.Err(err).Object("stream", stream).Msgf(format, v...)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue