mirror of
https://github.com/dcarrillo/whatismyip.git
synced 2026-07-23 22:45:46 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
04d6927cf5
|
|||
| 49a63ef071 | |||
|
a36a5504fa
|
|||
| 2e32a20f60 | |||
|
3874f6af3f
|
|||
| ff97f2c550 | |||
| 49ffcadb0a | |||
|
210e8c7cdb
|
+92
-16
@@ -27,17 +27,26 @@ jobs:
|
||||
- name: ${{ matrix.make }}
|
||||
run: make ${{ matrix.make }}
|
||||
|
||||
deploy:
|
||||
build-binaries:
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
strategy:
|
||||
matrix:
|
||||
goosarch: [linux-amd64]
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: install go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
@@ -47,27 +56,94 @@ jobs:
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Sign in to dockerhub
|
||||
run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
|
||||
|
||||
- name: Deploy image
|
||||
run: make docker-push VERSION=$RELEASE_VERSION
|
||||
|
||||
- name: Build
|
||||
run: make build VERSION=$RELEASE_VERSION
|
||||
run: make build VERSION="$RELEASE_VERSION" GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }}
|
||||
|
||||
- name: Prepare release
|
||||
- name: Package
|
||||
run: |
|
||||
BASE="whatismyip-$RELEASE_VERSION-${{ matrix.goos }}-${{ matrix.goarch }}"
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||
cp whatismyip "${BASE}.exe"
|
||||
zip "${BASE}.zip" "${BASE}.exe" LICENSE README.md
|
||||
sha256sum "${BASE}.zip" > "${BASE}.zip.sha256"
|
||||
else
|
||||
tar zcvf "${BASE}.tar.gz" whatismyip LICENSE README.md
|
||||
sha256sum "${BASE}.tar.gz" > "${BASE}.tar.gz.sha256"
|
||||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: binary-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
whatismyip-${{ env.RELEASE_VERSION }}-${{ matrix.goos }}-${{ matrix.goarch }}.*
|
||||
if-no-files-found: error
|
||||
|
||||
publish-docker:
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Sign in to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: ARG_VERSION=${{ env.RELEASE_VERSION }}
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/whatismyip:${{ env.RELEASE_VERSION }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/whatismyip:latest
|
||||
push: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-binaries, publish-docker]
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
pattern: binary-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Generate changelog
|
||||
run: |
|
||||
git log $(git describe HEAD~ --tags --abbrev=0)..HEAD --pretty='format:%h - %s <%an>' --no-merges > changelog.txt
|
||||
tar zcvf whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz whatismyip LICENSE README.md
|
||||
sha256sum whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz > whatismyip-$RELEASE_VERSION-${{matrix.goosarch}}.tar.gz.sha256
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
body_path: changelog.txt
|
||||
files: |
|
||||
whatismyip-${{env.RELEASE_VERSION}}-${{matrix.goosarch}}.tar.gz
|
||||
whatismyip-${{env.RELEASE_VERSION}}-${{matrix.goosarch}}.tar.gz.sha256
|
||||
whatismyip-${{ env.RELEASE_VERSION }}-*.tar.gz
|
||||
whatismyip-${{ env.RELEASE_VERSION }}-*.tar.gz.sha256
|
||||
whatismyip-${{ env.RELEASE_VERSION }}-*.zip
|
||||
whatismyip-${{ env.RELEASE_VERSION }}-*.zip.sha256
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
whatismyip
|
||||
dist/
|
||||
|
||||
@@ -2,6 +2,9 @@ version: "2"
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- errcheck
|
||||
- errorlint
|
||||
- govet
|
||||
- ineffassign
|
||||
- nakedret
|
||||
- revive
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# whatismyip
|
||||
|
||||
Single-binary Go service: HTTP/TLS/QUIC server that returns the client's IP, geolocation, ASN, headers, TCP port scan, and DNS discovery. Uses [gin](https://github.com/gin-gonic/gin) + [httprouter](https://github.com/julienschmidt/httprouter). Requires Go >= 1.25.
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
make build # CGO_ENABLED=0 -> ./whatismyip (GOOS/GOARCH for cross-compile)
|
||||
make build-all # cross-compile all 5 platforms into dist/
|
||||
make unit-test # go test -count=1 -race -short -cover ./...
|
||||
make integration-test # requires Docker; runs integration-tests/ module
|
||||
make test # unit-test + integration-test
|
||||
make lint # gofmt -l + golangci-lint + shadow (auto-installs tools)
|
||||
make docker-build # local single-arch build (root Dockerfile)
|
||||
make docker-run # builds dev image + runs with test DBs, ports :8080 :8081 :9100
|
||||
```
|
||||
|
||||
**Order and quirks**: `lint -> unit-test -> integration-test`. Integration tests use testcontainers-go (needs Docker). They are a separate Go module with its own `go.mod` — run from the `integration-tests/` directory. They use `test/Dockerfile` (distinct from the root `Dockerfile`).
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
cmd/whatismyip.go # entrypoint: parses flags, wires servers, runs
|
||||
├── server/server.go # Manager: Start/Stop/SIGHUP-reload for all server types
|
||||
├── server/tcp.go # plain HTTP
|
||||
├── server/tls.go # TLS/HTTP2
|
||||
├── server/quic.go # HTTP/3 (requires TLS server)
|
||||
├── server/dns.go # micro-DNS server for discovery
|
||||
├── server/prometheus.go # separate metrics endpoint
|
||||
├── router/setup.go # Gin route registration
|
||||
│ ├── router/generic.go # /, /client-port, /all, /json
|
||||
│ ├── router/geo.go # /geo/*, /asn/*
|
||||
│ ├── router/headers.go # /headers, /:header
|
||||
│ ├── router/dns.go # DNS discovery HTTP handler
|
||||
│ ├── router/port_scanner.go # /scan/tcp/:port
|
||||
│ └── router/templates.go # embedded HTML template
|
||||
├── service/geo.go # GeoIP lookup service (MaxMind MMDB)
|
||||
├── service/port_scanner.go
|
||||
├── resolver/setup.go # authoritative micro-DNS server (miekg/dns)
|
||||
├── internal/setting/ # flag parsing + resolver YAML config
|
||||
├── internal/httputils/ # header filtering and formatting
|
||||
├── internal/metrics/ # Prometheus counters/histograms (opt-in)
|
||||
├── internal/validator/uuid/
|
||||
├── internal/core/version.go
|
||||
└── models/geo.go # GeoDB wrapper around oschwald/maxminddb-golang
|
||||
```
|
||||
|
||||
## Key facts
|
||||
|
||||
- **DNS discovery**: enabled via `-resolver test/resolver.yml`. The resolver is an authoritative DNS server answering for a subdomain. Clients prove their DNS provider by resolving `<uuid>.dns.<domain>`.
|
||||
- **Geo**: both `-geoip2-city` and `-geoip2-asn` required together (or omitted). Uses test DBs in `test/`.
|
||||
- **Trusted headers**: `-trusted-header X-Real-IP` for proxy mode. When set without `-trusted-port-header`, client port shows "unknown".
|
||||
- **SIGHUP**: reloads GeoIP databases and restarts all servers without full stop.
|
||||
- **HTTP/3**: requires `-tls-bind`; reuses its port for UDP.
|
||||
- **Prometheus**: separate process/port via `-metrics-bind`. Metrics prefixed `whatismyip_*`.
|
||||
- **Port scan**: enabled by default; disable with `-disable-scan`.
|
||||
- **Embedded template**: `router/templates.go` has the default `home` template. `-template` overrides.
|
||||
- **Version**: injected at build via `-ldflags="-X 'github.com/dcarrillo/whatismyip/internal/core.Version=${VERSION}'"`.
|
||||
|
||||
## Testing quirks
|
||||
|
||||
- Integration tests need Docker (testcontainers-go) and will take longer. They run against a real containerized build.
|
||||
- Unit tests in `internal/setting/`, `internal/httputils/`, `internal/metrics/`, `internal/validator/uuid/`, `models/`, `router/`, and `service/` use test DBs from `test/`.
|
||||
- The router test reads `test/` resources by relative path — run from repo root.
|
||||
|
||||
## Lint
|
||||
|
||||
Uses `golangci-lint` v2 config (`.golangci.yaml`) with strict `revive` rules and `goimports` as formatter. One exception: `internal/metrics/` gets a pass on `var-naming` (package name conflicts with stdlib `metrics`). Also runs `shadow` and `gofmt -l -d`.
|
||||
+9
-18
@@ -1,29 +1,20 @@
|
||||
FROM golang:1.25-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
|
||||
|
||||
ARG ARG_VERSION
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ENV VERSION=$ARG_VERSION
|
||||
|
||||
WORKDIR /app
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ go mod download -x
|
||||
COPY . .
|
||||
|
||||
FROM builder AS build-dev-app
|
||||
# hadolint ignore=DL3018
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ apk --no-cache add make && make build
|
||||
|
||||
FROM builder AS build-prod-app
|
||||
# hadolint ignore=DL3018
|
||||
RUN apk --no-cache update && apk add --no-cache ca-certificates make upx \
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ apk --no-cache add make ca-certificates \
|
||||
&& update-ca-certificates \
|
||||
&& make build \
|
||||
&& upx --best --lzma whatismyip
|
||||
&& GOOS=$TARGETOS GOARCH=$TARGETARCH make build
|
||||
|
||||
FROM scratch AS dev
|
||||
COPY --from=build-dev-app /app/whatismyip /usr/bin/
|
||||
ENTRYPOINT ["whatismyip"]
|
||||
|
||||
FROM scratch AS prod
|
||||
COPY --from=build-prod-app /app/whatismyip /usr/bin/
|
||||
FROM scratch
|
||||
COPY --from=builder /app/whatismyip /usr/bin/
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
ENTRYPOINT ["whatismyip"]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
GOPATH ?= $(shell go env GOPATH)
|
||||
VERSION ?= devel-$(shell git rev-parse --short HEAD)
|
||||
DOCKER_URL ?= dcarrillo/whatismyip
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
GOARCH ?= $(shell go env GOARCH)
|
||||
|
||||
.PHONY: test
|
||||
test: unit-test integration-test
|
||||
@@ -9,7 +11,7 @@ unit-test:
|
||||
go test -count=1 -race -short -cover ./...
|
||||
|
||||
integration-test:
|
||||
go test -count=1 -v ./integration-tests
|
||||
cd integration-tests && go test -count=1 -v ./...
|
||||
|
||||
install-tools:
|
||||
@if ! command -v golangci-lint &> /dev/null; then \
|
||||
@@ -32,25 +34,20 @@ lint: install-tools
|
||||
shadow ./...
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build -ldflags="-s -w -X 'github.com/dcarrillo/whatismyip/internal/core.Version=${VERSION}'" -o whatismyip ./cmd
|
||||
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags="-s -w -X 'github.com/dcarrillo/whatismyip/internal/core.Version=${VERSION}'" -o whatismyip ./cmd
|
||||
|
||||
docker-build-dev:
|
||||
docker build --target=dev --build-arg=ARG_VERSION="${VERSION}" --tag ${DOCKER_URL}:${VERSION} .
|
||||
build-all:
|
||||
@mkdir -p dist
|
||||
GOOS=linux GOARCH=amd64 $(MAKE) build && mv whatismyip dist/whatismyip-linux-amd64
|
||||
GOOS=linux GOARCH=arm64 $(MAKE) build && mv whatismyip dist/whatismyip-linux-arm64
|
||||
GOOS=darwin GOARCH=amd64 $(MAKE) build && mv whatismyip dist/whatismyip-darwin-amd64
|
||||
GOOS=darwin GOARCH=arm64 $(MAKE) build && mv whatismyip dist/whatismyip-darwin-arm64
|
||||
GOOS=windows GOARCH=amd64 $(MAKE) build && mv whatismyip dist/whatismyip-windows-amd64.exe
|
||||
|
||||
docker-build-prod:
|
||||
docker build --target=prod --build-arg=ARG_VERSION="${VERSION}" --tag ${DOCKER_URL}:${VERSION} .
|
||||
docker-build:
|
||||
docker build --build-arg=ARG_VERSION="${VERSION}" --tag ${DOCKER_URL}:${VERSION} .
|
||||
|
||||
docker-push: docker-build-prod
|
||||
ifneq (,$(findstring devel-,$(VERSION)))
|
||||
@echo "VERSION is set to ${VERSION}, I can't push devel builds"
|
||||
exit 1
|
||||
else
|
||||
docker push ${DOCKER_URL}:${VERSION}
|
||||
docker tag ${DOCKER_URL}:${VERSION} ${DOCKER_URL}:latest
|
||||
docker push ${DOCKER_URL}:latest
|
||||
endif
|
||||
|
||||
docker-run: docker-build-dev
|
||||
docker-run: docker-build
|
||||
docker run --tty --interactive --rm \
|
||||
--publish 8080:8080/tcp \
|
||||
--publish 8081:8081/tcp \
|
||||
|
||||
@@ -120,7 +120,7 @@ curl $(cat /proc/sys/kernel/random/uuid).dns.ifconfig.es
|
||||
|
||||
## Build
|
||||
|
||||
Golang >= 1.24 is required.
|
||||
Golang >= 1.25 is required.
|
||||
|
||||
`make build`
|
||||
|
||||
@@ -202,7 +202,7 @@ Download the latest version from [github](https://github.com/dcarrillo/whatismyi
|
||||
|
||||
## Docker
|
||||
|
||||
An ultra-light (~6MB) image is available on [docker hub](https://hub.docker.com/r/dcarrillo/whatismyip). Since version `2.1.2`, the binary is compressed using [upx](https://github.com/upx/upx).
|
||||
An ultra-light (~6MB) image is available on [docker hub](https://hub.docker.com/r/dcarrillo/whatismyip). ~Since version `2.1.2`, the binary is compressed using [upx](https://github.com/upx/upx).~
|
||||
|
||||
### Run a container locally using test databases
|
||||
|
||||
|
||||
+54
-34
@@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
@@ -13,50 +15,64 @@ import (
|
||||
"github.com/dcarrillo/whatismyip/internal/metrics"
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/dcarrillo/whatismyip/resolver"
|
||||
"github.com/dcarrillo/whatismyip/router"
|
||||
"github.com/dcarrillo/whatismyip/server"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-contrib/secure"
|
||||
"github.com/patrickmn/go-cache"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/router"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
o, err := setting.Setup(os.Args[1:])
|
||||
cfg, o, err := setting.Setup(os.Args[1:])
|
||||
if err != nil {
|
||||
if err == flag.ErrHelp || err == setting.ErrVersion {
|
||||
if errors.Is(err, flag.ErrHelp) || errors.Is(err, setting.ErrVersion) {
|
||||
fmt.Print(o)
|
||||
os.Exit(0)
|
||||
}
|
||||
fmt.Println(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
servers := []server.Server{}
|
||||
engine := setupEngine()
|
||||
|
||||
if setting.App.Resolver.Domain != "" {
|
||||
store := cache.New(1*time.Minute, 10*time.Minute)
|
||||
dnsEngine := resolver.Setup(store)
|
||||
nameServer := server.NewDNSServer(context.Background(), dnsEngine.Handler())
|
||||
servers = append(servers, nameServer)
|
||||
engine.Use(router.GetDNSDiscoveryHandler(store, setting.App.Resolver.Domain, setting.App.Resolver.RedirectPort))
|
||||
}
|
||||
|
||||
var geoSvc *service.Geo
|
||||
if setting.App.GeodbPath.City != "" || setting.App.GeodbPath.ASN != "" {
|
||||
if geoSvc, err = service.NewGeo(context.Background(), setting.App.GeodbPath.City, setting.App.GeodbPath.ASN); err != nil {
|
||||
panic(err)
|
||||
if cfg.GeodbPath.City != "" || cfg.GeodbPath.ASN != "" {
|
||||
if geoSvc, err = service.NewGeo(context.Background(), cfg.GeodbPath.City, cfg.GeodbPath.ASN); err != nil {
|
||||
log.Fatalf("Failed to load geo databases: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
router.SetupTemplate(engine)
|
||||
router.Setup(engine, geoSvc)
|
||||
servers = slices.Concat(servers, setupHTTPServers(context.Background(), engine.Handler()))
|
||||
servers := []server.Server{}
|
||||
engine := setupEngine(cfg)
|
||||
|
||||
if setting.App.PrometheusAddress != "" {
|
||||
prometheusServer := server.NewPrometheusServer(context.Background())
|
||||
if cfg.Resolver.Domain != "" {
|
||||
store := cache.New(1*time.Minute, 10*time.Minute)
|
||||
var dnsEngine *resolver.Resolver
|
||||
if dnsEngine, err = resolver.Setup(store, resolver.Settings{
|
||||
Domain: cfg.Resolver.Domain,
|
||||
ResourceRecords: cfg.Resolver.ResourceRecords,
|
||||
RedirectPort: cfg.Resolver.RedirectPort,
|
||||
IPv4: cfg.Resolver.Ipv4,
|
||||
IPv6: cfg.Resolver.Ipv6,
|
||||
}); err != nil {
|
||||
log.Fatalf("Invalid resolver configuration: %s", err)
|
||||
}
|
||||
nameServer := server.NewDNSServer(context.Background(), dnsEngine.Handler())
|
||||
servers = append(servers, nameServer)
|
||||
engine.Use(router.GetDNSDiscoveryHandler(store, geoSvc, cfg.Resolver.Domain, cfg.Resolver.RedirectPort))
|
||||
}
|
||||
|
||||
rt := router.NewRouter(geoSvc, cfg.TrustedHeader, cfg.TrustedPortHeader, cfg.TemplatePath, cfg.DisableTCPScan)
|
||||
router.SetupTemplate(engine, cfg.TemplatePath)
|
||||
router.Setup(engine, rt)
|
||||
servers = slices.Concat(servers, setupHTTPServers(context.Background(), engine.Handler(), cfg))
|
||||
|
||||
if cfg.PrometheusAddress != "" {
|
||||
prometheusServer := server.NewPrometheusServer(context.Background(), cfg.PrometheusAddress,
|
||||
server.Timeouts{
|
||||
ReadTimeout: cfg.Server.ReadTimeout,
|
||||
WriteTimeout: cfg.Server.WriteTimeout,
|
||||
})
|
||||
servers = append(servers, prometheusServer)
|
||||
}
|
||||
|
||||
@@ -64,18 +80,18 @@ func main() {
|
||||
whatismyip.Run()
|
||||
}
|
||||
|
||||
func setupEngine() *gin.Engine {
|
||||
func setupEngine(cfg setting.Settings) *gin.Engine {
|
||||
gin.DisableConsoleColor()
|
||||
if os.Getenv(gin.EnvGinMode) == "" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
engine := gin.New()
|
||||
engine.Use(gin.LoggerWithFormatter(httputils.GetLogFormatter), gin.Recovery())
|
||||
if setting.App.PrometheusAddress != "" {
|
||||
if cfg.PrometheusAddress != "" {
|
||||
metrics.Enable()
|
||||
engine.Use(metrics.GinMiddleware())
|
||||
}
|
||||
if setting.App.EnableSecureHeaders {
|
||||
if cfg.EnableSecureHeaders {
|
||||
engine.Use(secure.New(secure.Config{
|
||||
BrowserXssFilter: true,
|
||||
ContentTypeNosniff: true,
|
||||
@@ -83,24 +99,28 @@ func setupEngine() *gin.Engine {
|
||||
}))
|
||||
}
|
||||
_ = engine.SetTrustedProxies(nil)
|
||||
engine.TrustedPlatform = setting.App.TrustedHeader
|
||||
engine.TrustedPlatform = cfg.TrustedHeader
|
||||
|
||||
return engine
|
||||
}
|
||||
|
||||
func setupHTTPServers(ctx context.Context, handler http.Handler) []server.Server {
|
||||
func setupHTTPServers(ctx context.Context, handler http.Handler, cfg setting.Settings) []server.Server {
|
||||
var servers []server.Server
|
||||
timeouts := server.Timeouts{
|
||||
ReadTimeout: cfg.Server.ReadTimeout,
|
||||
WriteTimeout: cfg.Server.WriteTimeout,
|
||||
}
|
||||
|
||||
if setting.App.BindAddress != "" {
|
||||
tcpServer := server.NewTCPServer(ctx, &handler)
|
||||
if cfg.BindAddress != "" {
|
||||
tcpServer := server.NewTCPServer(ctx, handler, cfg.BindAddress, timeouts)
|
||||
servers = append(servers, tcpServer)
|
||||
}
|
||||
|
||||
if setting.App.TLSAddress != "" {
|
||||
tlsServer := server.NewTLSServer(ctx, &handler)
|
||||
if cfg.TLSAddress != "" {
|
||||
tlsServer := server.NewTLSServer(ctx, handler, cfg.TLSAddress, cfg.TLSCrtPath, cfg.TLSKeyPath, timeouts)
|
||||
servers = append(servers, tlsServer)
|
||||
if setting.App.EnableHTTP3 {
|
||||
quicServer := server.NewQuicServer(ctx, tlsServer)
|
||||
if cfg.EnableHTTP3 {
|
||||
quicServer := server.NewQuicServer(ctx, tlsServer, cfg.TLSAddress, cfg.TLSCrtPath, cfg.TLSKeyPath)
|
||||
servers = append(servers, quicServer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ module github.com/dcarrillo/whatismyip
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/docker/docker v28.0.4+incompatible
|
||||
github.com/gin-contrib/secure v1.1.3
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -11,96 +10,51 @@ require (
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/quic-go/quic-go v0.59.1
|
||||
github.com/quic-go/quic-go v0.60.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/testcontainers/testcontainers-go v0.36.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic v1.15.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // 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.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gin-contrib/sse v1.1.1 // 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.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.2 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.3 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.21 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.23 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
github.com/prometheus/common v0.70.0 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
golang.org/x/arch v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/net v0.53.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect
|
||||
golang.org/x/arch v0.29.0 // indirect
|
||||
golang.org/x/crypto v0.54.0 // indirect
|
||||
golang.org/x/mod v0.38.0 // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
golang.org/x/tools v0.48.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
@@ -1,48 +1,18 @@
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo=
|
||||
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
|
||||
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
|
||||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
|
||||
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
|
||||
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
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=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/secure v1.1.3 h1:ryqoSpBR1giw585Wy7/3WqG70HCEj/GovB6RdGPRcvg=
|
||||
@@ -51,43 +21,29 @@ github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko
|
||||
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
|
||||
github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
|
||||
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
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.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
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/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -96,179 +52,80 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ=
|
||||
github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
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/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI=
|
||||
github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
|
||||
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0=
|
||||
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
|
||||
github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
golang.org/x/arch v0.26.0 h1:jZ6dpec5haP/fUv1kLCbuJy6dnRrfX6iVK08lZBFpk4=
|
||||
golang.org/x/arch v0.26.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
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.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
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=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
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=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
||||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho=
|
||||
golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
|
||||
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -277,5 +134,3 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
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=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
module github.com/dcarrillo/whatismyip/integration-tests
|
||||
|
||||
go 1.25.0
|
||||
|
||||
replace github.com/dcarrillo/whatismyip => ..
|
||||
|
||||
require (
|
||||
github.com/dcarrillo/whatismyip v0.0.0-00010101000000-000000000000
|
||||
github.com/docker/docker v28.0.4+incompatible
|
||||
github.com/quic-go/quic-go v0.60.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/testcontainers/testcontainers-go v0.36.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/bytedance/sonic v1.15.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // 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.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gin-contrib/sse v1.1.1 // indirect
|
||||
github.com/gin-gonic/gin v1.12.0 // 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.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.3 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.23 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.70.0 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
golang.org/x/arch v0.29.0 // indirect
|
||||
golang.org/x/crypto v0.54.0 // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260715232425-e75dac1f907d // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,273 @@
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo=
|
||||
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
|
||||
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
|
||||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
|
||||
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
|
||||
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
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=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
|
||||
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
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.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
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/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ=
|
||||
github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
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/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI=
|
||||
github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0=
|
||||
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
|
||||
github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho=
|
||||
golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
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/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=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
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=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260715232425-e75dac1f907d h1:Jkpk39hlTZOIp3RbfvNX9R8Hv+Sw0X89nlU/xFOErsc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260715232425-e75dac1f907d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
|
||||
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
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=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
@@ -142,7 +142,11 @@ func TestContainerIntegration(t *testing.T) {
|
||||
Started: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { c.Terminate(ctx) })
|
||||
t.Cleanup(func() {
|
||||
if err := c.Terminate(ctx); err != nil {
|
||||
t.Logf("Failed to terminate container: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
tests := []struct {
|
||||
@@ -301,7 +305,11 @@ func TestContainerIntegrationDisableScan(t *testing.T) {
|
||||
Started: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { c.Terminate(ctx) })
|
||||
t.Cleanup(func() {
|
||||
if err := c.Terminate(ctx); err != nil {
|
||||
t.Logf("Failed to terminate container: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RequestScanEndpointWithDisabledScan", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://localhost:8000/scan/tcp/8000", nil)
|
||||
|
||||
+10
-16
@@ -7,38 +7,32 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// HeadersToSortedString shorts and dumps http.Header to a string separated by \n
|
||||
// HeadersToSortedString sorts and dumps http.Header to a string separated by \n
|
||||
func HeadersToSortedString(headers http.Header) string {
|
||||
var output string
|
||||
|
||||
keys := make([]string, 0, len(headers))
|
||||
for k := range headers {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var output strings.Builder
|
||||
for _, k := range keys {
|
||||
if len(headers[k]) > 1 {
|
||||
for _, h := range headers[k] {
|
||||
output += k + ": " + h + "\n"
|
||||
}
|
||||
} else {
|
||||
output += k + ": " + headers[k][0] + "\n"
|
||||
for _, h := range headers[k] {
|
||||
output.WriteString(k + ": " + h + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
return output.String()
|
||||
}
|
||||
|
||||
// GetHeadersWithoutTrustedHeaders return a http.Heade object with the original headers except trusted headers
|
||||
func GetHeadersWithoutTrustedHeaders(ctx *gin.Context) http.Header {
|
||||
h := ctx.Request.Header
|
||||
// GetHeadersWithoutTrustedHeaders returns a copy of the request headers with the trusted headers removed
|
||||
func GetHeadersWithoutTrustedHeaders(ctx *gin.Context, trustedHeader, trustedPortHeader string) http.Header {
|
||||
h := ctx.Request.Header.Clone()
|
||||
|
||||
for _, k := range []string{setting.App.TrustedHeader, setting.App.TrustedPortHeader} {
|
||||
for _, k := range []string{trustedHeader, trustedPortHeader} {
|
||||
delete(h, textproto.CanonicalMIMEHeaderKey(k))
|
||||
}
|
||||
|
||||
@@ -49,7 +43,7 @@ func GetHeadersWithoutTrustedHeaders(ctx *gin.Context) http.Header {
|
||||
func GetLogFormatter(param gin.LogFormatterParams) string {
|
||||
return fmt.Sprintf("%s - [%s] \"%s %s %s\" %d %d %d %s \"%s\" \"%s\" \"%s\"\n",
|
||||
param.ClientIP,
|
||||
param.TimeStamp.Format("02/Nov/2006:15:04:05 -0700"),
|
||||
param.TimeStamp.Format("02/Jan/2006:15:04:05 -0700"),
|
||||
param.Method,
|
||||
param.Path,
|
||||
param.Request.Proto,
|
||||
|
||||
@@ -25,7 +25,7 @@ Header3: Three
|
||||
}
|
||||
|
||||
func TestGetLogFormatter(t *testing.T) {
|
||||
expected := "127.0.0.1 - [01/Nov/0001:00:00:00 +0000] \"GET / HTTP/1.1\" 200 100 1000 local \"golang test 1.0\" \"1.1.1.1, 2.2.2.2\" \"-\"\n"
|
||||
expected := "127.0.0.1 - [15/Jul/2022:10:30:00 +0000] \"GET / HTTP/1.1\" 200 100 1000 local \"golang test 1.0\" \"1.1.1.1, 2.2.2.2\" \"-\"\n"
|
||||
|
||||
h := http.Header{}
|
||||
h.Set("User-Agent", "golang test 1.0")
|
||||
@@ -39,7 +39,7 @@ func TestGetLogFormatter(t *testing.T) {
|
||||
|
||||
p := gin.LogFormatterParams{
|
||||
ClientIP: "127.0.0.1",
|
||||
TimeStamp: time.Time{},
|
||||
TimeStamp: time.Date(2022, time.July, 15, 10, 30, 0, 0, time.UTC),
|
||||
Method: "GET",
|
||||
Path: "/",
|
||||
StatusCode: 200,
|
||||
|
||||
+42
-46
@@ -13,9 +13,8 @@ import (
|
||||
)
|
||||
|
||||
type geodbConf struct {
|
||||
City string
|
||||
ASN string
|
||||
Token *string
|
||||
City string
|
||||
ASN string
|
||||
}
|
||||
type serverSettings struct {
|
||||
ReadTimeout time.Duration
|
||||
@@ -30,7 +29,7 @@ type resolver struct {
|
||||
Ipv6 []string `yaml:"ipv6,omitempty"`
|
||||
}
|
||||
|
||||
type settings struct {
|
||||
type Settings struct {
|
||||
GeodbPath geodbConf
|
||||
TemplatePath string
|
||||
BindAddress string
|
||||
@@ -52,75 +51,72 @@ const defaultAddress = ":8080"
|
||||
|
||||
var ErrVersion = errors.New("setting: version requested")
|
||||
|
||||
var App = settings{
|
||||
// hard-coded for the time being
|
||||
Server: serverSettings{
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
func Setup(args []string) (output string, err error) {
|
||||
func Setup(args []string) (cfg Settings, output string, err error) {
|
||||
flags := flag.NewFlagSet("whatismyip", flag.ContinueOnError)
|
||||
var buf bytes.Buffer
|
||||
var resolverConf string
|
||||
flags.SetOutput(&buf)
|
||||
|
||||
flags.StringVar(&App.GeodbPath.City, "geoip2-city", "", "Path to GeoIP2 city database. Enables geo information (--geoip2-asn becomes mandatory)")
|
||||
flags.StringVar(&App.GeodbPath.ASN, "geoip2-asn", "", "Path to GeoIP2 ASN database. Enables ASN information. (--geoip2-city becomes mandatory)")
|
||||
flags.StringVar(&App.TemplatePath, "template", "", "Path to the template file")
|
||||
cfg.Server = serverSettings{
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
flags.StringVar(&cfg.GeodbPath.City, "geoip2-city", "", "Path to GeoIP2 city database. Enables geo information (--geoip2-asn becomes mandatory)")
|
||||
flags.StringVar(&cfg.GeodbPath.ASN, "geoip2-asn", "", "Path to GeoIP2 ASN database. Enables ASN information. (--geoip2-city becomes mandatory)")
|
||||
flags.StringVar(&cfg.TemplatePath, "template", "", "Path to the template file")
|
||||
flags.StringVar(
|
||||
&resolverConf,
|
||||
"resolver",
|
||||
"",
|
||||
"Path to the resolver configuration. It actually enables the resolver for DNS client discovery.")
|
||||
flags.StringVar(
|
||||
&App.BindAddress,
|
||||
&cfg.BindAddress,
|
||||
"bind",
|
||||
defaultAddress,
|
||||
"Listening address (see https://pkg.go.dev/net?#Listen)",
|
||||
)
|
||||
flags.StringVar(
|
||||
&App.TLSAddress,
|
||||
&cfg.TLSAddress,
|
||||
"tls-bind",
|
||||
"",
|
||||
"Listening address for TLS (see https://pkg.go.dev/net?#Listen)",
|
||||
)
|
||||
flags.StringVar(&App.TLSCrtPath, "tls-crt", "", "When using TLS, path to certificate file")
|
||||
flags.StringVar(&App.TLSKeyPath, "tls-key", "", "When using TLS, path to private key file")
|
||||
flags.StringVar(&cfg.TLSCrtPath, "tls-crt", "", "When using TLS, path to certificate file")
|
||||
flags.StringVar(&cfg.TLSKeyPath, "tls-key", "", "When using TLS, path to private key file")
|
||||
flags.StringVar(
|
||||
&App.PrometheusAddress,
|
||||
&cfg.PrometheusAddress,
|
||||
"metrics-bind",
|
||||
"",
|
||||
"Listening address for Prometheus metrics endpoint (see https://pkg.go.dev/net?#Listen). It enables the metrics available at the given address/port via the /metrics endpoint.",
|
||||
)
|
||||
flags.StringVar(
|
||||
&App.TrustedHeader,
|
||||
&cfg.TrustedHeader,
|
||||
"trusted-header",
|
||||
"",
|
||||
"Trusted request header for remote IP (e.g. X-Real-IP). When using this feature if -trusted-port-header is not set the client port is shown as 'unknown'",
|
||||
)
|
||||
flags.StringVar(
|
||||
&App.TrustedPortHeader,
|
||||
&cfg.TrustedPortHeader,
|
||||
"trusted-port-header",
|
||||
"",
|
||||
"Trusted request header for remote client port (e.g. X-Real-Port). When this parameter is set -trusted-header becomes mandatory",
|
||||
)
|
||||
flags.BoolVar(&App.version, "version", false, "Output version information and exit")
|
||||
flags.BoolVar(&cfg.version, "version", false, "Output version information and exit")
|
||||
flags.BoolVar(
|
||||
&App.EnableSecureHeaders,
|
||||
&cfg.EnableSecureHeaders,
|
||||
"enable-secure-headers",
|
||||
false,
|
||||
"Add sane security-related headers to every response",
|
||||
)
|
||||
flags.BoolVar(
|
||||
&App.EnableHTTP3,
|
||||
&cfg.EnableHTTP3,
|
||||
"enable-http3",
|
||||
false,
|
||||
"Enable HTTP/3 protocol. HTTP/3 requires --tls-bind set, as HTTP/3 starts as a TLS connection that then gets upgraded to UDP. The UDP port is the same as the one used for the TLS server.",
|
||||
)
|
||||
flags.BoolVar(
|
||||
&App.DisableTCPScan,
|
||||
&cfg.DisableTCPScan,
|
||||
"disable-scan",
|
||||
false,
|
||||
"Disable TCP port scanning functionality",
|
||||
@@ -128,48 +124,48 @@ func Setup(args []string) (output string, err error) {
|
||||
|
||||
err = flags.Parse(args)
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
return cfg, buf.String(), err
|
||||
}
|
||||
|
||||
if App.version {
|
||||
return fmt.Sprintf("whatismyip version %s", core.Version), ErrVersion
|
||||
if cfg.version {
|
||||
return cfg, fmt.Sprintf("whatismyip version %s", core.Version), ErrVersion
|
||||
}
|
||||
|
||||
if (App.GeodbPath.City != "" && App.GeodbPath.ASN == "") || (App.GeodbPath.City == "" && App.GeodbPath.ASN != "") {
|
||||
return "", fmt.Errorf("both --geoip2-city and --geoip2-asn are mandatory to enable geo information")
|
||||
if (cfg.GeodbPath.City != "" && cfg.GeodbPath.ASN == "") || (cfg.GeodbPath.City == "" && cfg.GeodbPath.ASN != "") {
|
||||
return cfg, "", errors.New("both --geoip2-city and --geoip2-asn are mandatory to enable geo information")
|
||||
}
|
||||
|
||||
if App.TrustedPortHeader != "" && App.TrustedHeader == "" {
|
||||
return "", fmt.Errorf("truster-header is mandatory when truster-port-header is set")
|
||||
if cfg.TrustedPortHeader != "" && cfg.TrustedHeader == "" {
|
||||
return cfg, "", errors.New("trusted-header is mandatory when trusted-port-header is set")
|
||||
}
|
||||
|
||||
if (App.TLSAddress != "") && (App.TLSCrtPath == "" || App.TLSKeyPath == "") {
|
||||
return "", fmt.Errorf("in order to use TLS, the -tls-crt and -tls-key flags are mandatory")
|
||||
if (cfg.TLSAddress != "") && (cfg.TLSCrtPath == "" || cfg.TLSKeyPath == "") {
|
||||
return cfg, "", errors.New("in order to use TLS, the -tls-crt and -tls-key flags are mandatory")
|
||||
}
|
||||
|
||||
if App.EnableHTTP3 && App.TLSAddress == "" {
|
||||
return "", fmt.Errorf("in order to use HTTP3, the -tls-bind is mandatory")
|
||||
if cfg.EnableHTTP3 && cfg.TLSAddress == "" {
|
||||
return cfg, "", errors.New("in order to use HTTP3, the -tls-bind is mandatory")
|
||||
}
|
||||
|
||||
if App.TemplatePath != "" {
|
||||
info, err := os.Stat(App.TemplatePath)
|
||||
if os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("%s no such file or directory", App.TemplatePath)
|
||||
if cfg.TemplatePath != "" {
|
||||
info, err := os.Stat(cfg.TemplatePath)
|
||||
if err != nil {
|
||||
return cfg, "", fmt.Errorf("template path: %w", err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return "", fmt.Errorf("%s must be a file", App.TemplatePath)
|
||||
return cfg, "", fmt.Errorf("%s must be a file", cfg.TemplatePath)
|
||||
}
|
||||
}
|
||||
|
||||
if resolverConf != "" {
|
||||
var err error
|
||||
App.Resolver, err = readYAML(resolverConf)
|
||||
cfg.Resolver, err = readYAML(resolverConf)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading resolver configuration %w", err)
|
||||
return cfg, "", fmt.Errorf("reading resolver configuration: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
return cfg, buf.String(), nil
|
||||
}
|
||||
|
||||
func readYAML(path string) (resolver resolver, err error) {
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestParseMandatoryFlags(t *testing.T) {
|
||||
|
||||
for _, tt := range mandatoryFlags {
|
||||
t.Run(strings.Join(tt.args, " "), func(t *testing.T) {
|
||||
_, err := Setup(tt.args)
|
||||
_, _, err := Setup(tt.args)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "mandatory")
|
||||
})
|
||||
@@ -65,11 +65,11 @@ func TestParseMandatoryFlags(t *testing.T) {
|
||||
func TestParseFlags(t *testing.T) {
|
||||
flags := []struct {
|
||||
args []string
|
||||
conf settings
|
||||
conf Settings
|
||||
}{
|
||||
{
|
||||
[]string{},
|
||||
settings{
|
||||
Settings{
|
||||
BindAddress: ":8080",
|
||||
Server: serverSettings{
|
||||
ReadTimeout: 10 * time.Second,
|
||||
@@ -79,7 +79,7 @@ func TestParseFlags(t *testing.T) {
|
||||
},
|
||||
{
|
||||
[]string{"-disable-scan"},
|
||||
settings{
|
||||
Settings{
|
||||
BindAddress: ":8080",
|
||||
Server: serverSettings{
|
||||
ReadTimeout: 10 * time.Second,
|
||||
@@ -90,7 +90,7 @@ func TestParseFlags(t *testing.T) {
|
||||
},
|
||||
{
|
||||
[]string{"-bind", ":8001", "-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path"},
|
||||
settings{
|
||||
Settings{
|
||||
GeodbPath: geodbConf{
|
||||
City: "/city-path",
|
||||
ASN: "/asn-path",
|
||||
@@ -107,7 +107,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path", "-tls-bind", ":9000",
|
||||
"-tls-crt", "/crt-path", "-tls-key", "/key-path",
|
||||
},
|
||||
settings{
|
||||
Settings{
|
||||
GeodbPath: geodbConf{
|
||||
City: "/city-path",
|
||||
ASN: "/asn-path",
|
||||
@@ -127,7 +127,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path",
|
||||
"-trusted-header", "header", "-trusted-port-header", "port-header",
|
||||
},
|
||||
settings{
|
||||
Settings{
|
||||
GeodbPath: geodbConf{
|
||||
City: "/city-path",
|
||||
ASN: "/asn-path",
|
||||
@@ -146,7 +146,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"-geoip2-city", "/city-path", "-geoip2-asn", "/asn-path",
|
||||
"-trusted-header", "header", "-enable-secure-headers",
|
||||
},
|
||||
settings{
|
||||
Settings{
|
||||
GeodbPath: geodbConf{
|
||||
City: "/city-path",
|
||||
ASN: "/asn-path",
|
||||
@@ -164,9 +164,9 @@ func TestParseFlags(t *testing.T) {
|
||||
|
||||
for _, tt := range flags {
|
||||
t.Run(strings.Join(tt.args, " "), func(t *testing.T) {
|
||||
_, err := Setup(tt.args)
|
||||
cfg, _, err := Setup(tt.args)
|
||||
require.Nil(t, err)
|
||||
assert.True(t, reflect.DeepEqual(App, tt.conf))
|
||||
assert.True(t, reflect.DeepEqual(cfg, tt.conf))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ func TestParseFlagsUsage(t *testing.T) {
|
||||
|
||||
for _, arg := range usageArgs {
|
||||
t.Run(arg, func(t *testing.T) {
|
||||
output, err := Setup([]string{arg})
|
||||
_, output, err := Setup([]string{arg})
|
||||
assert.ErrorIs(t, err, flag.ErrHelp)
|
||||
assert.Contains(t, output, "Usage of")
|
||||
})
|
||||
@@ -184,7 +184,7 @@ func TestParseFlagsUsage(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseFlagVersion(t *testing.T) {
|
||||
output, err := Setup([]string{"-version"})
|
||||
_, output, err := Setup([]string{"-version"})
|
||||
assert.ErrorIs(t, err, ErrVersion)
|
||||
assert.Contains(t, output, "whatismyip version")
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func TestParseFlagTemplate(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := Setup(tc.flags)
|
||||
_, _, err := Setup(tc.flags)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tc.errMsg)
|
||||
})
|
||||
|
||||
+23
-21
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
@@ -53,39 +54,40 @@ func Setup(cityPath string, asnPath string) (*GeoDB, error) {
|
||||
}
|
||||
|
||||
func (db *GeoDB) CloseDBs() error {
|
||||
var errs []error
|
||||
return closeReaders(db.City, db.ASN)
|
||||
}
|
||||
|
||||
if db.City != nil {
|
||||
if err := db.City.Close(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("closing city db: %w", err))
|
||||
}
|
||||
func (db *GeoDB) Reload() error {
|
||||
city, asn, err := openDatabases(db.cityPath, db.asnPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening new databases: %w", err)
|
||||
}
|
||||
|
||||
if db.ASN != nil {
|
||||
if err := db.ASN.Close(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("closing ASN db: %w", err))
|
||||
}
|
||||
}
|
||||
oldCity, oldASN := db.City, db.ASN
|
||||
db.City, db.ASN = city, asn
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("errors closing databases: %s", errs)
|
||||
if err := closeReaders(oldCity, oldASN); err != nil {
|
||||
return fmt.Errorf("closing previous databases: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *GeoDB) Reload() error {
|
||||
if err := db.CloseDBs(); err != nil {
|
||||
return fmt.Errorf("closing existing connections: %w", err)
|
||||
func closeReaders(city *maxminddb.Reader, asn *maxminddb.Reader) error {
|
||||
var errs []error
|
||||
|
||||
if city != nil {
|
||||
if err := city.Close(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("closing city db: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
city, asn, err := openDatabases(db.cityPath, db.asnPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening new connections: %w", err)
|
||||
if asn != nil {
|
||||
if err := asn.Close(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("closing ASN db: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
db.City = city
|
||||
db.ASN = asn
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (db *GeoDB) LookupCity(ip net.IP) (*GeoRecord, error) {
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ func TestModels(t *testing.T) {
|
||||
|
||||
db, err := Setup("../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
require.NoError(t, err, fmt.Sprintf("Error setting up db: %s", err))
|
||||
defer db.CloseDBs()
|
||||
t.Cleanup(func() { assert.NoError(t, db.CloseDBs()) })
|
||||
assert.NotNil(t, db.ASN)
|
||||
assert.NotNil(t, db.City)
|
||||
|
||||
|
||||
+55
-31
@@ -1,22 +1,30 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/metrics"
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/dcarrillo/whatismyip/internal/validator/uuid"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Domain string
|
||||
ResourceRecords []string
|
||||
RedirectPort string
|
||||
IPv4 []string
|
||||
IPv6 []string
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
handler *dns.ServeMux
|
||||
store *cache.Cache
|
||||
domain string
|
||||
rr []string
|
||||
rr []dns.RR
|
||||
ipv4 []net.IP
|
||||
ipv6 []net.IP
|
||||
}
|
||||
@@ -28,27 +36,51 @@ func ensureDotSuffix(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func Setup(store *cache.Cache) *Resolver {
|
||||
var ipv4, ipv6 []net.IP
|
||||
for _, ip := range setting.App.Resolver.Ipv4 {
|
||||
ipv4 = append(ipv4, net.ParseIP(ip))
|
||||
func Setup(store *cache.Cache, cfg Settings) (*Resolver, error) {
|
||||
domain := ensureDotSuffix(cfg.Domain)
|
||||
|
||||
rr := make([]dns.RR, 0, len(cfg.ResourceRecords))
|
||||
for _, res := range cfg.ResourceRecords {
|
||||
record, err := dns.NewRR(domain + " " + res)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing resource record %q: %w", res, err)
|
||||
}
|
||||
rr = append(rr, record)
|
||||
}
|
||||
for _, ip := range setting.App.Resolver.Ipv6 {
|
||||
ipv6 = append(ipv6, net.ParseIP(ip))
|
||||
|
||||
ipv4, err := parseIPs(cfg.IPv4)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing ipv4 addresses: %w", err)
|
||||
}
|
||||
ipv6, err := parseIPs(cfg.IPv6)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing ipv6 addresses: %w", err)
|
||||
}
|
||||
|
||||
resolver := &Resolver{
|
||||
handler: dns.NewServeMux(),
|
||||
store: store,
|
||||
domain: ensureDotSuffix(setting.App.Resolver.Domain),
|
||||
rr: setting.App.Resolver.ResourceRecords,
|
||||
domain: domain,
|
||||
rr: rr,
|
||||
ipv4: ipv4,
|
||||
ipv6: ipv6,
|
||||
}
|
||||
resolver.handler.HandleFunc(resolver.domain, resolver.resolve)
|
||||
resolver.handler.HandleFunc(".", resolver.blackHole)
|
||||
|
||||
return resolver
|
||||
return resolver, nil
|
||||
}
|
||||
|
||||
func parseIPs(addresses []string) ([]net.IP, error) {
|
||||
ips := make([]net.IP, 0, len(addresses))
|
||||
for _, address := range addresses {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid IP address %q", address)
|
||||
}
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func (rsv *Resolver) Handler() *dns.ServeMux {
|
||||
@@ -58,7 +90,7 @@ func (rsv *Resolver) Handler() *dns.ServeMux {
|
||||
func (rsv *Resolver) blackHole(w dns.ResponseWriter, r *dns.Msg) {
|
||||
msg := startReply(r)
|
||||
msg.SetRcode(r, dns.RcodeRefused)
|
||||
w.WriteMsg(msg)
|
||||
writeMsg(w, msg)
|
||||
logger(w, r.Question[0], msg.Rcode)
|
||||
metrics.RecordDNSQuery(dns.TypeToString[r.Question[0].Qtype], dns.RcodeToString[msg.Rcode])
|
||||
}
|
||||
@@ -69,17 +101,10 @@ func (rsv *Resolver) resolve(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ip, _, _ := net.SplitHostPort(w.RemoteAddr().String())
|
||||
|
||||
for _, res := range rsv.rr {
|
||||
t := strings.Split(res, " ")[2]
|
||||
if q.Qtype == dns.StringToType[t] {
|
||||
brr, err := buildRR(rsv.domain + " " + res)
|
||||
if err != nil {
|
||||
msg.SetRcode(r, dns.RcodeServerFailure)
|
||||
logger(w, q, msg.Rcode, err.Error())
|
||||
} else {
|
||||
msg.Answer = append(msg.Answer, brr)
|
||||
logger(w, q, msg.Rcode)
|
||||
}
|
||||
w.WriteMsg(msg)
|
||||
if q.Qtype == res.Header().Rrtype {
|
||||
msg.Answer = append(msg.Answer, dns.Copy(res))
|
||||
writeMsg(w, msg)
|
||||
logger(w, q, msg.Rcode)
|
||||
metrics.RecordDNSQuery(dns.TypeToString[q.Qtype], dns.RcodeToString[msg.Rcode])
|
||||
return
|
||||
}
|
||||
@@ -90,14 +115,16 @@ func (rsv *Resolver) resolve(w dns.ResponseWriter, r *dns.Msg) {
|
||||
switch {
|
||||
case uuid.IsValid(subDomain):
|
||||
msg.SetRcode(r, rsv.getIP(q, msg))
|
||||
rsv.store.Add(subDomain, ip, cache.DefaultExpiration)
|
||||
// Add fails when the uuid is already registered; keep the first seen
|
||||
// resolver IP for the discovery window
|
||||
_ = rsv.store.Add(subDomain, ip, cache.DefaultExpiration)
|
||||
case lowerName == rsv.domain:
|
||||
msg.SetRcode(r, rsv.getIP(q, msg))
|
||||
default:
|
||||
msg.SetRcode(r, dns.RcodeRefused)
|
||||
}
|
||||
|
||||
w.WriteMsg(msg)
|
||||
writeMsg(w, msg)
|
||||
logger(w, q, msg.Rcode)
|
||||
metrics.RecordDNSQuery(dns.TypeToString[q.Qtype], dns.RcodeToString[msg.Rcode])
|
||||
}
|
||||
@@ -126,13 +153,10 @@ func (rsv *Resolver) getIP(question dns.Question, msg *dns.Msg) int {
|
||||
return dns.RcodeRefused
|
||||
}
|
||||
|
||||
func buildRR(rrs string) (dns.RR, error) {
|
||||
rr, err := dns.NewRR(rrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func writeMsg(w dns.ResponseWriter, msg *dns.Msg) {
|
||||
if err := w.WriteMsg(msg); err != nil {
|
||||
log.Printf("Failed to write DNS response: %s", err)
|
||||
}
|
||||
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
func setHdr(q dns.Question) dns.RR_Header {
|
||||
|
||||
+19
-11
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
validator "github.com/dcarrillo/whatismyip/internal/validator/uuid"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/patrickmn/go-cache"
|
||||
@@ -27,25 +28,33 @@ type dnsData struct {
|
||||
|
||||
// TODO
|
||||
// Implement a proper vhost manager instead of using a middleware
|
||||
func GetDNSDiscoveryHandler(store *cache.Cache, domain string, redirectPort string) gin.HandlerFunc {
|
||||
func GetDNSDiscoveryHandler(store *cache.Cache, geoSvc *service.Geo, domain string, redirectPort string) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
if !strings.HasSuffix(ctx.Request.Host, domain) {
|
||||
host := hostWithoutPort(ctx.Request.Host)
|
||||
if host != domain && !strings.HasSuffix(host, "."+domain) {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Request.Host == domain && ctx.Request.URL.Path == "/" {
|
||||
if host == domain && ctx.Request.URL.Path == "/" {
|
||||
ctx.Redirect(http.StatusFound, fmt.Sprintf("http://%s.%s%s", uuid.New().String(), domain, redirectPort))
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
handleDNS(ctx, store)
|
||||
handleDNS(ctx, store, geoSvc)
|
||||
ctx.Abort()
|
||||
}
|
||||
}
|
||||
|
||||
func handleDNS(ctx *gin.Context, store *cache.Cache) {
|
||||
func hostWithoutPort(host string) string {
|
||||
if h, _, err := net.SplitHostPort(host); err == nil {
|
||||
return h
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func handleDNS(ctx *gin.Context, store *cache.Cache, geoSvc *service.Geo) {
|
||||
d := strings.Split(ctx.Request.Host, ".")[0]
|
||||
if !validator.IsValid(d) {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
@@ -72,12 +81,11 @@ func handleDNS(ctx *gin.Context, store *cache.Cache) {
|
||||
|
||||
geoResp := dnsGeoData{}
|
||||
if geoSvc != nil {
|
||||
cityRecord := geoSvc.LookUpCity(ip)
|
||||
asnRecord := geoSvc.LookUpASN(ip)
|
||||
|
||||
geoResp = dnsGeoData{
|
||||
Country: cityRecord.Country.Names["en"],
|
||||
AsnOrganization: asnRecord.AutonomousSystemOrganization,
|
||||
if cityRecord := geoSvc.LookUpCity(ip); cityRecord != nil {
|
||||
geoResp.Country = cityRecord.Country.Names["en"]
|
||||
}
|
||||
if asnRecord := geoSvc.LookUpASN(ip); asnRecord != nil {
|
||||
geoResp.AsnOrganization = asnRecord.AutonomousSystemOrganization
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
func TestGetDNSDiscoveryHandler(t *testing.T) {
|
||||
store := cache.New(cache.NoExpiration, cache.NoExpiration)
|
||||
handler := GetDNSDiscoveryHandler(store, domain, "")
|
||||
handler := GetDNSDiscoveryHandler(store, rt.geo, domain, "")
|
||||
|
||||
t.Run("calls next if host does not have domain suffix", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
@@ -100,13 +100,13 @@ func TestHandleDNS(t *testing.T) {
|
||||
req.Host = tt.subDomain + "." + domain
|
||||
|
||||
if tt.stored != "" {
|
||||
store.Add(tt.subDomain, tt.stored, cache.DefaultExpiration)
|
||||
store.Set(tt.subDomain, tt.stored, cache.DefaultExpiration)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
handleDNS(c, store)
|
||||
handleDNS(c, store, rt.geo)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
})
|
||||
}
|
||||
@@ -143,8 +143,8 @@ func TestAcceptDNSRequest(t *testing.T) {
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
store.Add(u, testIP.ipv4, cache.DefaultExpiration)
|
||||
handleDNS(c, store)
|
||||
store.Set(u, testIP.ipv4, cache.DefaultExpiration)
|
||||
handleDNS(c, store, rt.geo)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, tt.want, w.Body.String())
|
||||
|
||||
+39
-37
@@ -6,7 +6,6 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/httputils"
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -31,31 +30,31 @@ type JSONResponse struct {
|
||||
GeoResponse
|
||||
}
|
||||
|
||||
func getRoot(ctx *gin.Context) {
|
||||
func (rt *Router) getRoot(ctx *gin.Context) {
|
||||
switch ctx.NegotiateFormat(gin.MIMEPlain, gin.MIMEHTML, gin.MIMEJSON) {
|
||||
case gin.MIMEHTML:
|
||||
name := "home"
|
||||
if setting.App.TemplatePath != "" {
|
||||
name = filepath.Base(setting.App.TemplatePath)
|
||||
if rt.templatePath != "" {
|
||||
name = filepath.Base(rt.templatePath)
|
||||
}
|
||||
ctx.HTML(http.StatusOK, name, jsonOutput(ctx))
|
||||
ctx.HTML(http.StatusOK, name, rt.jsonOutput(ctx))
|
||||
case gin.MIMEJSON:
|
||||
getJSON(ctx)
|
||||
rt.getJSON(ctx)
|
||||
default:
|
||||
ctx.String(http.StatusOK, ctx.ClientIP()+"\n")
|
||||
}
|
||||
}
|
||||
|
||||
func getClientPort(ctx *gin.Context) string {
|
||||
func (rt *Router) getClientPort(ctx *gin.Context) string {
|
||||
var port string
|
||||
if setting.App.TrustedPortHeader == "" {
|
||||
if setting.App.TrustedHeader != "" {
|
||||
if rt.trustedPortHeader == "" {
|
||||
if rt.trustedHeader != "" {
|
||||
port = "unknown"
|
||||
} else {
|
||||
_, port, _ = net.SplitHostPort(ctx.Request.RemoteAddr)
|
||||
}
|
||||
} else {
|
||||
port = ctx.GetHeader(setting.App.TrustedPortHeader)
|
||||
port = ctx.GetHeader(rt.trustedPortHeader)
|
||||
if port == "" {
|
||||
port = "unknown"
|
||||
}
|
||||
@@ -64,33 +63,37 @@ func getClientPort(ctx *gin.Context) string {
|
||||
return port
|
||||
}
|
||||
|
||||
func getClientPortAsString(ctx *gin.Context) {
|
||||
ctx.String(http.StatusOK, getClientPort(ctx)+"\n")
|
||||
func (rt *Router) getClientPortAsString(ctx *gin.Context) {
|
||||
ctx.String(http.StatusOK, rt.getClientPort(ctx)+"\n")
|
||||
}
|
||||
|
||||
func getAllAsString(ctx *gin.Context) {
|
||||
func (rt *Router) getAllAsString(ctx *gin.Context) {
|
||||
ip := net.ParseIP(ctx.ClientIP())
|
||||
|
||||
output := "IP: " + ip.String() + "\n"
|
||||
output += "Client Port: " + getClientPort(ctx) + "\n"
|
||||
output += "Client Port: " + rt.getClientPort(ctx) + "\n"
|
||||
|
||||
if geoSvc != nil {
|
||||
output += geoCityRecordToString(geoSvc.LookUpCity(ip)) + "\n"
|
||||
output += geoASNRecordToString(geoSvc.LookUpASN(ip)) + "\n"
|
||||
if rt.geo != nil {
|
||||
if cityRecord := rt.geo.LookUpCity(ip); cityRecord != nil {
|
||||
output += geoCityRecordToString(cityRecord) + "\n"
|
||||
}
|
||||
if asnRecord := rt.geo.LookUpASN(ip); asnRecord != nil {
|
||||
output += geoASNRecordToString(asnRecord) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
h := httputils.GetHeadersWithoutTrustedHeaders(ctx)
|
||||
h := httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader)
|
||||
h.Set("Host", ctx.Request.Host)
|
||||
output += httputils.HeadersToSortedString(h)
|
||||
|
||||
ctx.String(http.StatusOK, output)
|
||||
}
|
||||
|
||||
func getJSON(ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, jsonOutput(ctx))
|
||||
func (rt *Router) getJSON(ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, rt.jsonOutput(ctx))
|
||||
}
|
||||
|
||||
func jsonOutput(ctx *gin.Context) JSONResponse {
|
||||
func (rt *Router) jsonOutput(ctx *gin.Context) JSONResponse {
|
||||
ip := net.ParseIP(ctx.ClientIP())
|
||||
|
||||
var version byte = 4
|
||||
@@ -99,29 +102,28 @@ func jsonOutput(ctx *gin.Context) JSONResponse {
|
||||
}
|
||||
|
||||
geoResp := GeoResponse{}
|
||||
if geoSvc != nil {
|
||||
cityRecord := geoSvc.LookUpCity(ip)
|
||||
asnRecord := geoSvc.LookUpASN(ip)
|
||||
|
||||
geoResp = GeoResponse{
|
||||
Country: cityRecord.Country.Names["en"],
|
||||
CountryCode: cityRecord.Country.ISOCode,
|
||||
City: cityRecord.City.Names["en"],
|
||||
Latitude: cityRecord.Location.Latitude,
|
||||
Longitude: cityRecord.Location.Longitude,
|
||||
PostalCode: cityRecord.Postal.Code,
|
||||
TimeZone: cityRecord.Location.TimeZone,
|
||||
ASN: asnRecord.AutonomousSystemNumber,
|
||||
ASNOrganization: asnRecord.AutonomousSystemOrganization,
|
||||
if rt.geo != nil {
|
||||
if cityRecord := rt.geo.LookUpCity(ip); cityRecord != nil {
|
||||
geoResp.Country = cityRecord.Country.Names["en"]
|
||||
geoResp.CountryCode = cityRecord.Country.ISOCode
|
||||
geoResp.City = cityRecord.City.Names["en"]
|
||||
geoResp.Latitude = cityRecord.Location.Latitude
|
||||
geoResp.Longitude = cityRecord.Location.Longitude
|
||||
geoResp.PostalCode = cityRecord.Postal.Code
|
||||
geoResp.TimeZone = cityRecord.Location.TimeZone
|
||||
}
|
||||
if asnRecord := rt.geo.LookUpASN(ip); asnRecord != nil {
|
||||
geoResp.ASN = asnRecord.AutonomousSystemNumber
|
||||
geoResp.ASNOrganization = asnRecord.AutonomousSystemOrganization
|
||||
}
|
||||
}
|
||||
|
||||
return JSONResponse{
|
||||
IP: ip.String(),
|
||||
IPVersion: version,
|
||||
ClientPort: getClientPort(ctx),
|
||||
ClientPort: rt.getClientPort(ctx),
|
||||
Host: ctx.Request.Host,
|
||||
Headers: httputils.GetHeadersWithoutTrustedHeaders(ctx),
|
||||
Headers: httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader),
|
||||
GeoResponse: geoResp,
|
||||
}
|
||||
}
|
||||
|
||||
+29
-41
@@ -1,12 +1,14 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -99,8 +101,9 @@ func TestHost(t *testing.T) {
|
||||
|
||||
func TestClientPort(t *testing.T) {
|
||||
type args struct {
|
||||
params []string
|
||||
headers map[string][]string
|
||||
trustedHeader string
|
||||
trustedPortHeader string
|
||||
headers map[string][]string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -114,35 +117,23 @@ func TestClientPort(t *testing.T) {
|
||||
{
|
||||
name: "Trusted header only set",
|
||||
args: args{
|
||||
params: []string{
|
||||
"-geoip2-city", "city",
|
||||
"-geoip2-asn", "asn",
|
||||
"-trusted-header", trustedHeader,
|
||||
},
|
||||
trustedHeader: trustedHeader,
|
||||
},
|
||||
expected: "unknown\n",
|
||||
},
|
||||
{
|
||||
name: "Trusted and port header set but not included in headers",
|
||||
args: args{
|
||||
params: []string{
|
||||
"-geoip2-city", "city",
|
||||
"-geoip2-asn", "asn",
|
||||
"-trusted-header", trustedHeader,
|
||||
"-trusted-port-header", trustedPortHeader,
|
||||
},
|
||||
trustedHeader: trustedHeader,
|
||||
trustedPortHeader: trustedPortHeader,
|
||||
},
|
||||
expected: "unknown\n",
|
||||
},
|
||||
{
|
||||
name: "Trusted and port header set and included in headers",
|
||||
args: args{
|
||||
params: []string{
|
||||
"-geoip2-city", "city",
|
||||
"-geoip2-asn", "asn",
|
||||
"-trusted-header", trustedHeader,
|
||||
"-trusted-port-header", trustedPortHeader,
|
||||
},
|
||||
trustedHeader: trustedHeader,
|
||||
trustedPortHeader: trustedPortHeader,
|
||||
headers: map[string][]string{
|
||||
trustedHeader: {testIP.ipv4},
|
||||
trustedPortHeader: {"1001"},
|
||||
@@ -153,19 +144,22 @@ func TestClientPort(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
_, _ = setting.Setup(tt.args.params)
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
engine := gin.Default()
|
||||
engine.TrustedPlatform = tt.args.trustedHeader
|
||||
r := NewRouter(nil, tt.args.trustedHeader, tt.args.trustedPortHeader, "", false)
|
||||
Setup(engine, r)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/client-port", nil)
|
||||
req.RemoteAddr = net.JoinHostPort(testIP.ipv4, "1000")
|
||||
req.Header = tt.args.headers
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, tt.expected, w.Body.String())
|
||||
t.Log(w.Header())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -181,14 +175,11 @@ func TestNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
_, _ = setting.Setup(
|
||||
[]string{
|
||||
"-geoip2-city", "city",
|
||||
"-geoip2-asn", "asn",
|
||||
"-trusted-header", trustedHeader,
|
||||
"-trusted-port-header", trustedPortHeader,
|
||||
},
|
||||
)
|
||||
svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
engine := gin.Default()
|
||||
engine.TrustedPlatform = trustedHeader
|
||||
r := NewRouter(svc, trustedHeader, trustedPortHeader, "", false)
|
||||
Setup(engine, r)
|
||||
|
||||
type args struct {
|
||||
ip string
|
||||
@@ -222,7 +213,7 @@ func TestJSON(t *testing.T) {
|
||||
req.Header.Set(trustedPortHeader, "1001")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.json, w.Header().Get("Content-Type"))
|
||||
@@ -248,14 +239,11 @@ ASN Organization:
|
||||
Header1: one
|
||||
Host: test
|
||||
`
|
||||
_, _ = setting.Setup(
|
||||
[]string{
|
||||
"-geoip2-city", "city",
|
||||
"-geoip2-asn", "asn",
|
||||
"-trusted-header", trustedHeader,
|
||||
"-trusted-port-header", trustedPortHeader,
|
||||
},
|
||||
)
|
||||
svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
engine := gin.Default()
|
||||
engine.TrustedPlatform = trustedHeader
|
||||
r := NewRouter(svc, trustedHeader, trustedPortHeader, "", false)
|
||||
Setup(engine, r)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/all", nil)
|
||||
req.RemoteAddr = net.JoinHostPort(testIP.ipv4, "1000")
|
||||
@@ -265,7 +253,7 @@ Host: test
|
||||
req.Header.Set("Header1", "one")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
|
||||
+34
-15
@@ -81,37 +81,56 @@ var asnOutput = map[string]asnDataFormatter{
|
||||
},
|
||||
}
|
||||
|
||||
func getGeoAsString(ctx *gin.Context) {
|
||||
if geoSvc == nil {
|
||||
func (rt *Router) getGeoAsString(ctx *gin.Context) {
|
||||
if rt.geo == nil {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
record := rt.geo.LookUpCity(net.ParseIP(ctx.ClientIP()))
|
||||
if record == nil {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
field := strings.ToLower(ctx.Params.ByName("field"))
|
||||
record := geoSvc.LookUpCity(net.ParseIP(ctx.ClientIP()))
|
||||
if field == "" {
|
||||
ctx.String(http.StatusOK, geoCityRecordToString(record))
|
||||
} else if g, ok := geoOutput[field]; ok {
|
||||
ctx.String(http.StatusOK, g.format(record))
|
||||
} else {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getASNAsString(ctx *gin.Context) {
|
||||
if geoSvc == nil {
|
||||
g, ok := geoOutput[field]
|
||||
if !ok {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
ctx.String(http.StatusOK, g.format(record))
|
||||
}
|
||||
|
||||
func (rt *Router) getASNAsString(ctx *gin.Context) {
|
||||
if rt.geo == nil {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
record := rt.geo.LookUpASN(net.ParseIP(ctx.ClientIP()))
|
||||
if record == nil {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
field := strings.ToLower(ctx.Params.ByName("field"))
|
||||
record := geoSvc.LookUpASN(net.ParseIP(ctx.ClientIP()))
|
||||
if field == "" {
|
||||
ctx.String(http.StatusOK, geoASNRecordToString(record))
|
||||
} else if g, ok := asnOutput[field]; ok {
|
||||
ctx.String(http.StatusOK, g.format(record))
|
||||
} else {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
g, ok := asnOutput[field]
|
||||
if !ok {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
ctx.String(http.StatusOK, g.format(record))
|
||||
}
|
||||
|
||||
func geoCityRecordToString(record *models.GeoRecord) string {
|
||||
|
||||
+4
-5
@@ -9,15 +9,14 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func getHeadersAsSortedString(ctx *gin.Context) {
|
||||
h := httputils.GetHeadersWithoutTrustedHeaders(ctx)
|
||||
func (rt *Router) getHeadersAsSortedString(ctx *gin.Context) {
|
||||
h := httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader)
|
||||
h.Set("Host", ctx.Request.Host)
|
||||
|
||||
ctx.String(http.StatusOK, httputils.HeadersToSortedString(h))
|
||||
}
|
||||
|
||||
func getHeaderAsString(ctx *gin.Context) {
|
||||
headers := httputils.GetHeadersWithoutTrustedHeaders(ctx)
|
||||
func (rt *Router) getHeaderAsString(ctx *gin.Context) {
|
||||
headers := httputils.GetHeadersWithoutTrustedHeaders(ctx, rt.trustedHeader, rt.trustedPortHeader)
|
||||
|
||||
h := ctx.Params.ByName("header")
|
||||
if v := headers.Get(ctx.Params.ByName("header")); v != "" {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -27,12 +29,11 @@ Header2: value22
|
||||
Header3: value3
|
||||
Host:
|
||||
`
|
||||
_, _ = setting.Setup([]string{
|
||||
"-geoip2-city", "city",
|
||||
"-geoip2-asn", "asn",
|
||||
"-trusted-header", trustedHeader,
|
||||
"-trusted-port-header", trustedPortHeader,
|
||||
})
|
||||
svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
engine := gin.Default()
|
||||
engine.TrustedPlatform = trustedHeader
|
||||
r := NewRouter(svc, trustedHeader, trustedPortHeader, "", false)
|
||||
Setup(engine, r)
|
||||
req, _ := http.NewRequest("GET", "/headers", nil)
|
||||
req.Header = map[string][]string{
|
||||
"Header1": {"value1"},
|
||||
@@ -43,7 +44,7 @@ Host:
|
||||
req.Header.Set(trustedPortHeader, "1025")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
|
||||
+10
-2
@@ -17,7 +17,7 @@ type JSONScanResponse struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
func scanTCPPort(ctx *gin.Context) {
|
||||
func (rt *Router) scanTCPPort(ctx *gin.Context) {
|
||||
port, err := strconv.Atoi(ctx.Params.ByName("port"))
|
||||
if err == nil && (port < 1 || port > 65535) {
|
||||
err = fmt.Errorf("%d is not a valid port number", port)
|
||||
@@ -29,8 +29,16 @@ func scanTCPPort(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ip := net.ParseIP(ctx.ClientIP())
|
||||
if ip == nil {
|
||||
ctx.JSON(http.StatusBadRequest, JSONScanResponse{
|
||||
Reason: "client ip could not be determined",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
add := net.TCPAddr{
|
||||
IP: net.ParseIP(ctx.ClientIP()),
|
||||
IP: ip,
|
||||
Port: port,
|
||||
}
|
||||
|
||||
|
||||
+35
-22
@@ -4,36 +4,49 @@ import (
|
||||
"html/template"
|
||||
"log"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var geoSvc *service.Geo
|
||||
type Router struct {
|
||||
geo *service.Geo
|
||||
trustedHeader string
|
||||
trustedPortHeader string
|
||||
templatePath string
|
||||
disableScan bool
|
||||
}
|
||||
|
||||
func SetupTemplate(r *gin.Engine) {
|
||||
if setting.App.TemplatePath == "" {
|
||||
t, _ := template.New("home").Parse(home)
|
||||
r.SetHTMLTemplate(t)
|
||||
func NewRouter(geo *service.Geo, trustedHeader, trustedPortHeader, templatePath string, disableScan bool) *Router {
|
||||
return &Router{
|
||||
geo: geo,
|
||||
trustedHeader: trustedHeader,
|
||||
trustedPortHeader: trustedPortHeader,
|
||||
templatePath: templatePath,
|
||||
disableScan: disableScan,
|
||||
}
|
||||
}
|
||||
|
||||
func SetupTemplate(r *gin.Engine, templatePath string) {
|
||||
if templatePath == "" {
|
||||
r.SetHTMLTemplate(template.Must(template.New("home").Parse(home)))
|
||||
} else {
|
||||
log.Printf("Template %s has been loaded", setting.App.TemplatePath)
|
||||
r.LoadHTMLFiles(setting.App.TemplatePath)
|
||||
log.Printf("Template %s has been loaded", templatePath)
|
||||
r.LoadHTMLFiles(templatePath)
|
||||
}
|
||||
}
|
||||
|
||||
func Setup(r *gin.Engine, geo *service.Geo) {
|
||||
geoSvc = geo
|
||||
r.GET("/", getRoot)
|
||||
if !setting.App.DisableTCPScan {
|
||||
r.GET("/scan/tcp/:port", scanTCPPort)
|
||||
func Setup(r *gin.Engine, rt *Router) {
|
||||
r.GET("/", rt.getRoot)
|
||||
if !rt.disableScan {
|
||||
r.GET("/scan/tcp/:port", rt.scanTCPPort)
|
||||
}
|
||||
r.GET("/client-port", getClientPortAsString)
|
||||
r.GET("/geo", getGeoAsString)
|
||||
r.GET("/geo/:field", getGeoAsString)
|
||||
r.GET("/asn", getASNAsString)
|
||||
r.GET("/asn/:field", getASNAsString)
|
||||
r.GET("/headers", getHeadersAsSortedString)
|
||||
r.GET("/all", getAllAsString)
|
||||
r.GET("/json", getJSON)
|
||||
r.GET("/:header", getHeaderAsString)
|
||||
r.GET("/client-port", rt.getClientPortAsString)
|
||||
r.GET("/geo", rt.getGeoAsString)
|
||||
r.GET("/geo/:field", rt.getGeoAsString)
|
||||
r.GET("/asn", rt.getASNAsString)
|
||||
r.GET("/asn/:field", rt.getASNAsString)
|
||||
r.GET("/headers", rt.getHeadersAsSortedString)
|
||||
r.GET("/all", rt.getAllAsString)
|
||||
r.GET("/json", rt.getJSON)
|
||||
r.GET("/:header", rt.getHeaderAsString)
|
||||
}
|
||||
|
||||
@@ -47,11 +47,14 @@ const (
|
||||
domain = "dns.example.com"
|
||||
)
|
||||
|
||||
var rt *Router
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
app = gin.Default()
|
||||
app.TrustedPlatform = trustedHeader
|
||||
svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
Setup(app, svc)
|
||||
rt = NewRouter(svc, trustedHeader, "", "", false)
|
||||
Setup(app, rt)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
+3
-1
@@ -42,7 +42,9 @@ func (d *DNS) Start() {
|
||||
|
||||
func (d *DNS) Stop() {
|
||||
log.Print("Stopping DNS server...")
|
||||
if err := d.server.Shutdown(); err != nil {
|
||||
ctx, cancel := context.WithTimeout(d.ctx, shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := d.server.ShutdownContext(ctx); err != nil {
|
||||
log.Printf("DNS server forced to shutdown: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-10
@@ -6,18 +6,21 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
type Prometheus struct {
|
||||
server *http.Server
|
||||
ctx context.Context
|
||||
server *http.Server
|
||||
ctx context.Context
|
||||
addr string
|
||||
timeouts Timeouts
|
||||
}
|
||||
|
||||
func NewPrometheusServer(ctx context.Context) *Prometheus {
|
||||
func NewPrometheusServer(ctx context.Context, addr string, timeouts Timeouts) *Prometheus {
|
||||
return &Prometheus{
|
||||
ctx: ctx,
|
||||
ctx: ctx,
|
||||
addr: addr,
|
||||
timeouts: timeouts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +29,13 @@ func (p *Prometheus) Start() {
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
p.server = &http.Server{
|
||||
Addr: setting.App.PrometheusAddress,
|
||||
Addr: p.addr,
|
||||
Handler: mux,
|
||||
ReadTimeout: setting.App.Server.ReadTimeout,
|
||||
WriteTimeout: setting.App.Server.WriteTimeout,
|
||||
ReadTimeout: p.timeouts.ReadTimeout,
|
||||
WriteTimeout: p.timeouts.WriteTimeout,
|
||||
}
|
||||
|
||||
log.Printf("Starting Prometheus server listening on %s", setting.App.PrometheusAddress)
|
||||
log.Printf("Starting Prometheus server listening on %s", p.addr)
|
||||
go func() {
|
||||
if err := p.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatal(err)
|
||||
@@ -42,7 +45,9 @@ func (p *Prometheus) Start() {
|
||||
|
||||
func (p *Prometheus) Stop() {
|
||||
log.Print("Stopping Prometheus server...")
|
||||
if err := p.server.Shutdown(p.ctx); err != nil {
|
||||
ctx, cancel := context.WithTimeout(p.ctx, shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := p.server.Shutdown(ctx); err != nil {
|
||||
log.Printf("Prometheus server forced to shutdown: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -6,7 +6,6 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
)
|
||||
|
||||
@@ -14,33 +13,39 @@ type Quic struct {
|
||||
server *http3.Server
|
||||
tlsServer *TLS
|
||||
ctx context.Context
|
||||
addr string
|
||||
crtPath string
|
||||
keyPath string
|
||||
}
|
||||
|
||||
func NewQuicServer(ctx context.Context, tlsServer *TLS) *Quic {
|
||||
func NewQuicServer(ctx context.Context, tlsServer *TLS, addr, crt, key string) *Quic {
|
||||
return &Quic{
|
||||
tlsServer: tlsServer,
|
||||
ctx: ctx,
|
||||
addr: addr,
|
||||
crtPath: crt,
|
||||
keyPath: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Quic) Start() {
|
||||
q.server = &http3.Server{
|
||||
Addr: setting.App.TLSAddress,
|
||||
Addr: q.addr,
|
||||
Handler: q.tlsServer.server.Handler,
|
||||
}
|
||||
|
||||
parentHandler := q.tlsServer.server.Handler
|
||||
q.tlsServer.server.Handler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if err := q.server.SetQUICHeaders(rw.Header()); err != nil {
|
||||
log.Fatal(err)
|
||||
log.Printf("Failed to set QUIC headers: %s", err)
|
||||
}
|
||||
|
||||
parentHandler.ServeHTTP(rw, req)
|
||||
})
|
||||
|
||||
log.Printf("Starting QUIC server listening on %s (udp)", setting.App.TLSAddress)
|
||||
log.Printf("Starting QUIC server listening on %s (udp)", q.addr)
|
||||
go func() {
|
||||
if err := q.server.ListenAndServeTLS(setting.App.TLSCrtPath, setting.App.TLSKeyPath); err != nil &&
|
||||
if err := q.server.ListenAndServeTLS(q.crtPath, q.keyPath); err != nil &&
|
||||
!errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -49,7 +54,9 @@ func (q *Quic) Start() {
|
||||
|
||||
func (q *Quic) Stop() {
|
||||
log.Print("Stopping QUIC server...")
|
||||
if err := q.server.Close(); err != nil {
|
||||
log.Print("QUIC server forced to shutdown")
|
||||
ctx, cancel := context.WithTimeout(q.ctx, shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := q.server.Shutdown(ctx); err != nil {
|
||||
log.Printf("QUIC server forced to shutdown: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -5,10 +5,13 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
)
|
||||
|
||||
const shutdownTimeout = 10 * time.Second
|
||||
|
||||
type Server interface {
|
||||
Start()
|
||||
Stop()
|
||||
@@ -29,11 +32,10 @@ func Setup(servers []Server, geoSvc *service.Geo) *Manager {
|
||||
func (m *Manager) Run() {
|
||||
m.start()
|
||||
|
||||
signalChan := make(chan os.Signal, len(m.servers))
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
var s os.Signal
|
||||
for {
|
||||
s = <-signalChan
|
||||
s := <-signalChan
|
||||
|
||||
if s == syscall.SIGHUP {
|
||||
m.stop()
|
||||
@@ -43,10 +45,10 @@ func (m *Manager) Run() {
|
||||
m.start()
|
||||
} else {
|
||||
log.Print("Shutting down...")
|
||||
m.stop()
|
||||
if m.geoSvc != nil {
|
||||
m.geoSvc.Shutdown()
|
||||
}
|
||||
m.stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
+25
-15
@@ -5,32 +5,40 @@ import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TCP struct {
|
||||
server *http.Server
|
||||
handler *http.Handler
|
||||
ctx context.Context
|
||||
type Timeouts struct {
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
}
|
||||
|
||||
func NewTCPServer(ctx context.Context, handler *http.Handler) *TCP {
|
||||
type TCP struct {
|
||||
server *http.Server
|
||||
handler http.Handler
|
||||
ctx context.Context
|
||||
addr string
|
||||
timeouts Timeouts
|
||||
}
|
||||
|
||||
func NewTCPServer(ctx context.Context, handler http.Handler, addr string, timeouts Timeouts) *TCP {
|
||||
return &TCP{
|
||||
handler: handler,
|
||||
ctx: ctx,
|
||||
handler: handler,
|
||||
ctx: ctx,
|
||||
addr: addr,
|
||||
timeouts: timeouts,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCP) Start() {
|
||||
t.server = &http.Server{
|
||||
Addr: setting.App.BindAddress,
|
||||
Handler: *t.handler,
|
||||
ReadTimeout: setting.App.Server.ReadTimeout,
|
||||
WriteTimeout: setting.App.Server.WriteTimeout,
|
||||
Addr: t.addr,
|
||||
Handler: t.handler,
|
||||
ReadTimeout: t.timeouts.ReadTimeout,
|
||||
WriteTimeout: t.timeouts.WriteTimeout,
|
||||
}
|
||||
|
||||
log.Printf("Starting TCP server listening on %s", setting.App.BindAddress)
|
||||
log.Printf("Starting TCP server listening on %s", t.addr)
|
||||
go func() {
|
||||
if err := t.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatal(err)
|
||||
@@ -40,7 +48,9 @@ func (t *TCP) Start() {
|
||||
|
||||
func (t *TCP) Stop() {
|
||||
log.Print("Stopping TCP server...")
|
||||
if err := t.server.Shutdown(t.ctx); err != nil {
|
||||
ctx, cancel := context.WithTimeout(t.ctx, shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := t.server.Shutdown(ctx); err != nil {
|
||||
log.Printf("TCP server forced to shutdown: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-15
@@ -2,37 +2,47 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
)
|
||||
|
||||
type TLS struct {
|
||||
server *http.Server
|
||||
handler *http.Handler
|
||||
ctx context.Context
|
||||
server *http.Server
|
||||
handler http.Handler
|
||||
ctx context.Context
|
||||
addr string
|
||||
crtPath string
|
||||
keyPath string
|
||||
timeouts Timeouts
|
||||
}
|
||||
|
||||
func NewTLSServer(ctx context.Context, handler *http.Handler) *TLS {
|
||||
func NewTLSServer(ctx context.Context, handler http.Handler, addr, crt, key string, timeouts Timeouts) *TLS {
|
||||
return &TLS{
|
||||
handler: handler,
|
||||
ctx: ctx,
|
||||
handler: handler,
|
||||
ctx: ctx,
|
||||
addr: addr,
|
||||
crtPath: crt,
|
||||
keyPath: key,
|
||||
timeouts: timeouts,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TLS) Start() {
|
||||
t.server = &http.Server{
|
||||
Addr: setting.App.TLSAddress,
|
||||
Handler: *t.handler,
|
||||
ReadTimeout: setting.App.Server.ReadTimeout,
|
||||
WriteTimeout: setting.App.Server.WriteTimeout,
|
||||
Addr: t.addr,
|
||||
Handler: t.handler,
|
||||
ReadTimeout: t.timeouts.ReadTimeout,
|
||||
WriteTimeout: t.timeouts.WriteTimeout,
|
||||
TLSConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
}
|
||||
|
||||
log.Printf("Starting TLS server listening on %s", setting.App.TLSAddress)
|
||||
log.Printf("Starting TLS server listening on %s", t.addr)
|
||||
go func() {
|
||||
if err := t.server.ListenAndServeTLS(setting.App.TLSCrtPath, setting.App.TLSKeyPath); err != nil &&
|
||||
if err := t.server.ListenAndServeTLS(t.crtPath, t.keyPath); err != nil &&
|
||||
!errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -41,7 +51,9 @@ func (t *TLS) Start() {
|
||||
|
||||
func (t *TLS) Stop() {
|
||||
log.Print("Stopping TLS server...")
|
||||
if err := t.server.Shutdown(t.ctx); err != nil {
|
||||
ctx, cancel := context.WithTimeout(t.ctx, shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := t.server.Shutdown(ctx); err != nil {
|
||||
log.Printf("TLS server forced to shutdown: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -36,6 +36,9 @@ func NewGeo(ctx context.Context, cityPath string, asnPath string) (*Geo, error)
|
||||
}
|
||||
|
||||
func (g *Geo) LookUpCity(ip net.IP) *models.GeoRecord {
|
||||
g.mu.RLock()
|
||||
defer g.mu.RUnlock()
|
||||
|
||||
record, err := g.db.LookupCity(ip)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -47,6 +50,9 @@ func (g *Geo) LookUpCity(ip net.IP) *models.GeoRecord {
|
||||
}
|
||||
|
||||
func (g *Geo) LookUpASN(ip net.IP) *models.ASNRecord {
|
||||
g.mu.RLock()
|
||||
defer g.mu.RUnlock()
|
||||
|
||||
record, err := g.db.LookupASN(ip)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -59,7 +65,13 @@ func (g *Geo) LookUpASN(ip net.IP) *models.ASNRecord {
|
||||
|
||||
func (g *Geo) Shutdown() {
|
||||
g.cancel()
|
||||
g.db.CloseDBs()
|
||||
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
|
||||
if err := g.db.CloseDBs(); err != nil {
|
||||
log.Printf("Error closing geo databases: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Geo) Reload() {
|
||||
@@ -71,6 +83,9 @@ func (g *Geo) Reload() {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
|
||||
g.db.Reload()
|
||||
if err := g.db.Reload(); err != nil {
|
||||
log.Printf("Geo database reload failed: %s", err)
|
||||
return
|
||||
}
|
||||
log.Print("Geo database reloaded")
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var geoSvc *Geo
|
||||
@@ -31,3 +33,35 @@ func TestASNLookup(t *testing.T) {
|
||||
a = geoSvc.LookUpASN(net.ParseIP("1.1.1.1"))
|
||||
assert.NotNil(t, a)
|
||||
}
|
||||
|
||||
func TestReloadIsSafeForConcurrentLookups(t *testing.T) {
|
||||
svc, err := NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
require.NoError(t, err)
|
||||
defer svc.Shutdown()
|
||||
|
||||
ip := net.ParseIP("81.2.69.192")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
stop := make(chan struct{})
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
default:
|
||||
svc.LookUpCity(ip)
|
||||
svc.LookUpASN(ip)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for range 100 {
|
||||
svc.Reload()
|
||||
}
|
||||
close(stop)
|
||||
wg.Wait()
|
||||
|
||||
assert.NotNil(t, svc.LookUpCity(ip))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user