diff --git a/agent/go.mod b/agent/go.mod index 543a696b..a37eb1bc 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -31,7 +31,6 @@ require ( github.com/PuerkitoBio/goquery v1.11.0 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect - github.com/buger/goterm v1.0.4 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect @@ -40,9 +39,7 @@ require ( github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/diskfs/go-diskfs v1.7.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/djherbis/times v1.6.0 // indirect github.com/docker/cli v29.1.3+incompatible // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -50,31 +47,23 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-acme/lego/v4 v4.30.1 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect - github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.1 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gotify/server/v2 v2.8.0 // indirect - github.com/jinzhu/copier v0.4.0 // indirect github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect - github.com/luthermonson/go-proxmox v0.3.1 // indirect - github.com/magefile/mage v1.15.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/dns v1.1.69 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/moby/api v1.52.0 // indirect github.com/moby/moby/client v0.2.1 // indirect @@ -82,7 +71,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // 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/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pires/go-proxyproto v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -95,7 +83,6 @@ require ( github.com/samber/slog-zerolog/v2 v2.9.0 // indirect github.com/shirou/gopsutil/v4 v4.25.12 // indirect github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect - github.com/spf13/afero v1.15.0 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -115,13 +102,9 @@ require ( go.opentelemetry.io/otel/trace v1.39.0 // indirect golang.org/x/arch v0.23.0 // indirect golang.org/x/crypto v0.46.0 // indirect - golang.org/x/mod v0.31.0 // indirect golang.org/x/net v0.48.0 // indirect - golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect - golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/agent/go.sum b/agent/go.sum index 573a3a70..26ba7305 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -1,38 +1,11 @@ -cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= -cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= -cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= -cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 h1:yzrctSl9GMIQ5lHu7jc8olOsGjWDCsBpJhWqfGa/YIM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0/go.mod h1:GE4m0rnnfwLGX0Y9A9A25Zx5N/90jneT5ABevqzhuFQ= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 h1:zLzoX5+W2l95UJoVwiyNS4dX8vHyQ6x2xRLoBBL9wMk= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= -github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 h1:h/33OxYLqBk0BYmEbSUy7MlvgQR/m1w1/7OJFKoPL1I= -github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0/go.mod h1:rvh3imDA6EaQi+oM/GQHkQAOHbXPKJ7EWJvfjuw141Q= -github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs= -github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= -github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= -github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= @@ -72,8 +45,6 @@ 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.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY= -github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -96,8 +67,6 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= -github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= 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= @@ -106,10 +75,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= -github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4= -github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= @@ -117,38 +82,20 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE= github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= -github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= -github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= -github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= 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/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= -github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotify/server/v2 v2.8.0 h1:E3UDDn/3rFZi1sjZfbuhXNnxJP3ACZhdcw/iySegPRA= github.com/gotify/server/v2 v2.8.0/go.mod h1:6ci5adxcE2hf1v+2oowKiQmixOxXV8vU+CRLKP6sqZA= -github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= -github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= -github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= @@ -161,12 +108,8 @@ 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/linode/linodego v1.63.0 h1:MdjizfXNJDVJU6ggoJmMO5O9h4KGPGivNX0fzrAnstk= -github.com/linode/linodego v1.63.0/go.mod h1:GoiwLVuLdBQcAebxAVKVL3mMYUgJZR/puOUSla04xBE= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= @@ -184,8 +127,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= @@ -197,33 +138,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w 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/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0= -github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg= -github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2 h1:l0tH15ACQADZAzC+LZ+mo2tIX4H6uZu0ulrVmG5Tqz0= -github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8= -github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2 h1:gzB4c6ztb38C/jYiqEaFC+mCGcWFHDji9e6jwymY9d4= -github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2/go.mod h1:l1qIPIq2uRV5WTSvkbhbl/ndbeOu7OCb3UZ+0+2ZSb8= -github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw= -github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54= 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/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE= -github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE= -github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -246,20 +171,14 @@ github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89 github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M= github.com/samber/slog-zerolog/v2 v2.9.0 h1:6LkOabJmZdNLaUWkTC3IVVA+dq7b/V0FM6lz6/7+THI= github.com/samber/slog-zerolog/v2 v2.9.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 h1:ObX9hZmK+VmijreZO/8x9pQ8/P/ToHD/bdSb4Eg4tUo= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36/go.mod h1:LEsDu4BubxK7/cWhtlQWfuxwL4rf/2UEpxXz1o1EMtM= github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0= github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= -github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= 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/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= 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= @@ -275,25 +194,17 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS 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/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= -github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok= github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= -github.com/vultr/govultr/v3 v3.26.1 h1:G/M0rMQKwVSmL+gb0UgETbW5mcQi0Vf/o/ZSGdBCxJw= -github.com/vultr/govultr/v3 v3.26.1/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusing/ds v0.3.1 h1:mCqTgTQD8RhiBpcysvii5kZ7ZBmqcknVsFubNALGLbY= github.com/yusing/ds v0.3.1/go.mod h1:XhKV4l7cZwBbbl7lRzNC9zX27zvCM0frIwiuD40ULRk= -github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20260104140148-1c2515cb298d h1:O6umnEZyKot6IqyOCuLMUuCT8/K8n+lKiQJ+UjmSfVc= -github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20260104140148-1c2515cb298d/go.mod h1:84uz4o4GfD4FhXv3v7620Vj7LtXL0gnxDgL9LA+KmEI= github.com/yusing/gointernals v0.1.16 h1:GrhZZdxzA+jojLEqankctJrOuAYDb7kY1C93S1pVR34= github.com/yusing/gointernals v0.1.16/go.mod h1:B/0FVXt4WPmgzVy3ynzkqKi+BSGaJVmwCJBRXYapo34= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -316,8 +227,6 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= -go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= -go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -361,10 +270,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -409,21 +316,11 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.258.0 h1:IKo1j5FBlN74fe5isA2PVozN3Y5pwNKriEgAXPOkDAc= -google.golang.org/api v0.258.0/go.mod h1:qhOMTQEZ6lUps63ZNq9jhODswwjkjYYguA7fA3TBFww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= 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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= diff --git a/agent/pkg/handler/check_health.go b/agent/pkg/handler/check_health.go index 16f82a15..d511e7de 100644 --- a/agent/pkg/handler/check_health.go +++ b/agent/pkg/handler/check_health.go @@ -1,16 +1,16 @@ package handler import ( - "context" - "fmt" + "net" "net/http" "net/url" - "os" + "strconv" "strings" + "time" "github.com/bytedance/sonic" "github.com/yusing/godoxy/internal/types" - "github.com/yusing/godoxy/internal/watcher/health/monitor" + healthcheck "github.com/yusing/godoxy/internal/watcher/health/check" ) func CheckHealth(w http.ResponseWriter, r *http.Request) { @@ -20,6 +20,7 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) { http.Error(w, "missing scheme", http.StatusBadRequest) return } + timeout := parseMsOrDefault(query.Get("timeout")) var ( result types.HealthCheckResult @@ -32,24 +33,21 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) { http.Error(w, "missing path", http.StatusBadRequest) return } - _, err := os.Stat(path) - result = types.HealthCheckResult{Healthy: err == nil} - if err != nil { - result.Detail = err.Error() - } - case "http", "https": // path is optional + result, err = healthcheck.FileServer(path) + case "http", "https", "h2c": // path is optional host := query.Get("host") path := query.Get("path") if host == "" { http.Error(w, "missing host", http.StatusBadRequest) return } - result, err = monitor.NewHTTPHealthMonitor(&url.URL{ - Scheme: scheme, - Host: host, - Path: path, - }, healthCheckConfigFromRequest(r)).CheckHealth() - case "tcp", "udp": + url := url.URL{Scheme: scheme, Host: host} + if scheme == "h2c" { + result, err = healthcheck.H2C(r.Context(), &url, http.MethodHead, path, timeout) + } else { + result, err = healthcheck.HTTP(&url, http.MethodHead, path, timeout) + } + case "tcp", "udp", "tcp4", "udp4", "tcp6", "udp6": host := query.Get("host") if host == "" { http.Error(w, "missing host", http.StatusBadRequest) @@ -62,12 +60,10 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) { return } if port != "" { - host = fmt.Sprintf("%s:%s", host, port) + host = net.JoinHostPort(host, port) } - result, err = monitor.NewRawHealthMonitor(&url.URL{ - Scheme: scheme, - Host: host, - }, healthCheckConfigFromRequest(r)).CheckHealth() + url := url.URL{Scheme: scheme, Host: host} + result, err = healthcheck.Stream(r.Context(), &url, timeout) } if err != nil { @@ -80,12 +76,15 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) { sonic.ConfigDefault.NewEncoder(w).Encode(result) } -func healthCheckConfigFromRequest(r *http.Request) types.HealthCheckConfig { - // we only need timeout and base context because it's one shot request - return types.HealthCheckConfig{ - Timeout: types.HealthCheckTimeoutDefault, - BaseContext: func() context.Context { - return r.Context() - }, +func parseMsOrDefault(msStr string) time.Duration { + if msStr == "" { + return types.HealthCheckTimeoutDefault } + + timeoutMs, _ := strconv.ParseInt(msStr, 10, 64) + if timeoutMs == 0 { + return types.HealthCheckTimeoutDefault + } + + return time.Duration(timeoutMs) * time.Millisecond } diff --git a/internal/docker/client.go b/internal/docker/client.go index de8669cb..428d6483 100644 --- a/internal/docker/client.go +++ b/internal/docker/client.go @@ -250,7 +250,7 @@ func (c *SharedClient) Key() string { return c.key } -func (c *SharedClient) Address() string { +func (c *SharedClient) DaemonHost() string { return c.addr } diff --git a/internal/watcher/docker_watcher.go b/internal/watcher/docker_watcher.go index 472d1fd9..77b8d2c7 100644 --- a/internal/watcher/docker_watcher.go +++ b/internal/watcher/docker_watcher.go @@ -104,7 +104,7 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList }() chs := client.Events(ctx, options) - defer log.Debug().Str("host", client.Address()).Msg("docker watcher closed") + defer log.Debug().Str("host", client.DaemonHost()).Msg("docker watcher closed") for { select { case <-ctx.Done(): @@ -177,7 +177,7 @@ func checkConnection(ctx context.Context, client *docker.SharedClient) bool { defer cancel() err := client.CheckConnection(ctx) if err != nil { - log.Debug().Err(err).Str("host", client.Address()).Msg("docker watcher: connection failed") + log.Debug().Err(err).Str("host", client.DaemonHost()).Msg("docker watcher: connection failed") return false } return true diff --git a/internal/watcher/health/check/docker.go b/internal/watcher/health/check/docker.go new file mode 100644 index 00000000..80567240 --- /dev/null +++ b/internal/watcher/health/check/docker.go @@ -0,0 +1,115 @@ +package healthcheck + +import ( + "context" + "errors" + "net/http" + "time" + + "github.com/bytedance/sonic" + "github.com/moby/moby/api/types/container" + "github.com/moby/moby/client" + "github.com/yusing/godoxy/internal/docker" + "github.com/yusing/godoxy/internal/types" + httputils "github.com/yusing/goutils/http" +) + +type DockerHealthcheckState struct { + client *docker.SharedClient + containerId string + + numDockerFailures int +} + +const dockerFailuresThreshold = 3 + +var errDockerHealthCheckFailedTooManyTimes = errors.New("docker health check failed too many times") + +func NewDockerHealthcheckState(client *docker.SharedClient, containerId string) *DockerHealthcheckState { + client.InterceptHTTPClient(interceptDockerInspectResponse) + return &DockerHealthcheckState{ + client: client, + containerId: containerId, + numDockerFailures: 0, + } +} + +func Docker(ctx context.Context, state *DockerHealthcheckState, containerId string, timeout time.Duration) (types.HealthCheckResult, error) { + if state.numDockerFailures > dockerFailuresThreshold { + return types.HealthCheckResult{}, errDockerHealthCheckFailedTooManyTimes + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // the actual inspect response is intercepted and returned as RequestInterceptedError + _, err := state.client.ContainerInspect(ctx, containerId, client.ContainerInspectOptions{}) + + var interceptedErr *httputils.RequestInterceptedError + if !httputils.AsRequestInterceptedError(err, &interceptedErr) { + state.numDockerFailures++ + return types.HealthCheckResult{}, err + } + + if interceptedErr == nil || interceptedErr.Data == nil { // should not happen + state.numDockerFailures++ + return types.HealthCheckResult{}, errors.New("intercepted error is nil or data is nil") + } + + containerState := interceptedErr.Data.(container.State) + + status := containerState.Status + switch status { + case "dead", "exited", "paused", "restarting", "removing": + state.numDockerFailures = 0 + return types.HealthCheckResult{ + Healthy: false, + Detail: "container is " + string(status), + }, nil + case "created": + state.numDockerFailures = 0 + return types.HealthCheckResult{ + Healthy: false, + Detail: "container is not started", + }, nil + } + + health := containerState.Health + if health == nil { + // no health check from docker, directly use fallback + state.numDockerFailures = dockerFailuresThreshold + 1 + return types.HealthCheckResult{}, errDockerHealthCheckFailedTooManyTimes + } + + state.numDockerFailures = 0 + result := types.HealthCheckResult{ + Healthy: health.Status == container.Healthy, + } + if len(health.Log) > 0 { + lastLog := health.Log[len(health.Log)-1] + result.Detail = lastLog.Output + result.Latency = lastLog.End.Sub(lastLog.Start) + } + return result, nil +} + +func interceptDockerInspectResponse(resp *http.Response) (intercepted bool, err error) { + if resp.StatusCode != http.StatusOK { + return false, nil + } + + body, release, err := httputils.ReadAllBody(resp) + resp.Body.Close() + if err != nil { + return false, err + } + + var state container.State + err = sonic.Unmarshal(body, &state) + release(body) + if err != nil { + return false, err + } + + return true, httputils.NewRequestInterceptedError(resp, state) +} diff --git a/internal/watcher/health/check/fileserver.go b/internal/watcher/health/check/fileserver.go new file mode 100644 index 00000000..ea16318f --- /dev/null +++ b/internal/watcher/health/check/fileserver.go @@ -0,0 +1,28 @@ +package healthcheck + +import ( + "os" + "time" + + "github.com/yusing/godoxy/internal/types" +) + +func FileServer(path string) (types.HealthCheckResult, error) { + start := time.Now() + _, err := os.Stat(path) + lat := time.Since(start) + + if err != nil { + if os.IsNotExist(err) { + return types.HealthCheckResult{ + Detail: err.Error(), + }, nil + } + return types.HealthCheckResult{}, err + } + + return types.HealthCheckResult{ + Healthy: true, + Latency: lat, + }, nil +} diff --git a/internal/watcher/health/monitor/http.go b/internal/watcher/health/check/http.go similarity index 69% rename from internal/watcher/health/monitor/http.go rename to internal/watcher/health/check/http.go index b286ca1d..44d69b84 100644 --- a/internal/watcher/health/monitor/http.go +++ b/internal/watcher/health/check/http.go @@ -1,4 +1,4 @@ -package monitor +package healthcheck import ( "context" @@ -15,11 +15,15 @@ import ( "golang.org/x/net/http2" ) -type HTTPHealthMonitor struct { - *monitor - method string +var h2cClient = &http.Client{ + Transport: &http2.Transport{ + AllowHTTP: true, + DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, network, addr) + }, + }, } - var pinger = &fasthttp.Client{ MaxConnDuration: 0, DisableHeaderNamesNormalizing: true, @@ -31,15 +35,56 @@ var pinger = &fasthttp.Client{ NoDefaultUserAgentHeader: true, } -func NewHTTPHealthMonitor(url *url.URL, config types.HealthCheckConfig) *HTTPHealthMonitor { - mon := new(HTTPHealthMonitor) - mon.monitor = newMonitor(url, config, mon.CheckHealth) - if config.UseGet { - mon.method = fasthttp.MethodGet +func HTTP(url *url.URL, method, path string, timeout time.Duration) (types.HealthCheckResult, error) { + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) + + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseResponse(resp) + + req.SetRequestURI(url.JoinPath(path).String()) + req.Header.SetMethod(method) + setCommonHeaders(req.Header.Set) + req.SetConnectionClose() + + start := time.Now() + respErr := pinger.DoTimeout(req, resp, timeout) + lat := time.Since(start) + + return processHealthResponse(lat, respErr, resp.StatusCode) +} + +func H2C(ctx context.Context, url *url.URL, method, path string, timeout time.Duration) (types.HealthCheckResult, error) { + u := url.JoinPath(path) // JoinPath returns a copy of the URL with the path joined + u.Scheme = "http" + + ctx, cancel := context.WithTimeoutCause(ctx, timeout, errors.New("h2c health check timed out")) + defer cancel() + + var req *http.Request + var err error + if method == fasthttp.MethodGet { + req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) } else { - mon.method = fasthttp.MethodHead + req, err = http.NewRequestWithContext(ctx, http.MethodHead, u.String(), nil) } - return mon + if err != nil { + return types.HealthCheckResult{ + Detail: err.Error(), + }, nil + } + + setCommonHeaders(req.Header.Set) + + start := time.Now() + resp, err := h2cClient.Do(req) + lat := time.Since(start) + + if resp != nil { + defer resp.Body.Close() + } + + return processHealthResponse(lat, err, func() int { return resp.StatusCode }) } var userAgent = "GoDoxy/" + version.Get().String() @@ -76,73 +121,3 @@ func processHealthResponse(lat time.Duration, err error, getStatusCode func() in Healthy: true, }, nil } - -var h2cClient = &http.Client{ - Transport: &http2.Transport{ - AllowHTTP: true, - DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) { - var d net.Dialer - return d.DialContext(ctx, network, addr) - }, - }, -} - -func (mon *HTTPHealthMonitor) CheckHealth() (types.HealthCheckResult, error) { - if mon.url.Load().Scheme == "h2c" { - return mon.CheckHealthH2C() - } - return mon.CheckHealthHTTP() -} - -func (mon *HTTPHealthMonitor) CheckHealthHTTP() (types.HealthCheckResult, error) { - req := fasthttp.AcquireRequest() - defer fasthttp.ReleaseRequest(req) - - resp := fasthttp.AcquireResponse() - defer fasthttp.ReleaseResponse(resp) - - req.SetRequestURI(mon.url.Load().JoinPath(mon.config.Path).String()) - req.Header.SetMethod(mon.method) - setCommonHeaders(req.Header.Set) - req.SetConnectionClose() - - start := time.Now() - respErr := pinger.DoTimeout(req, resp, mon.config.Timeout) - lat := time.Since(start) - - return processHealthResponse(lat, respErr, resp.StatusCode) -} - -func (mon *HTTPHealthMonitor) CheckHealthH2C() (types.HealthCheckResult, error) { - u := mon.url.Load() - u = u.JoinPath(mon.config.Path) // JoinPath returns a copy of the URL with the path joined - u.Scheme = "http" - - ctx, cancel := mon.ContextWithTimeout("h2c health check timed out") - defer cancel() - - var req *http.Request - var err error - if mon.method == fasthttp.MethodGet { - req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) - } else { - req, err = http.NewRequestWithContext(ctx, http.MethodHead, u.String(), nil) - } - if err != nil { - return types.HealthCheckResult{ - Detail: err.Error(), - }, nil - } - - setCommonHeaders(req.Header.Set) - - start := time.Now() - resp, err := h2cClient.Do(req) - lat := time.Since(start) - - if resp != nil { - defer resp.Body.Close() - } - - return processHealthResponse(lat, err, func() int { return resp.StatusCode }) -} diff --git a/internal/watcher/health/monitor/raw.go b/internal/watcher/health/check/stream.go similarity index 53% rename from internal/watcher/health/monitor/raw.go rename to internal/watcher/health/check/stream.go index fcde7255..6f4c9a16 100644 --- a/internal/watcher/health/monitor/raw.go +++ b/internal/watcher/health/check/stream.go @@ -1,6 +1,7 @@ -package monitor +package healthcheck import ( + "context" "errors" "net" "net/url" @@ -10,30 +11,17 @@ import ( "github.com/yusing/godoxy/internal/types" ) -type ( - RawHealthMonitor struct { - *monitor - dialer *net.Dialer - } -) - -func NewRawHealthMonitor(url *url.URL, config types.HealthCheckConfig) *RawHealthMonitor { - mon := new(RawHealthMonitor) - mon.monitor = newMonitor(url, config, mon.CheckHealth) - mon.dialer = &net.Dialer{ - Timeout: config.Timeout, +func Stream(ctx context.Context, url *url.URL, timeout time.Duration) (types.HealthCheckResult, error) { + dialer := net.Dialer{ + Timeout: timeout, FallbackDelay: -1, } - return mon -} -func (mon *RawHealthMonitor) CheckHealth() (types.HealthCheckResult, error) { - ctx, cancel := mon.ContextWithTimeout("ping request timed out") + ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - url := mon.url.Load() start := time.Now() - conn, err := mon.dialer.DialContext(ctx, url.Scheme, url.Host) + conn, err := dialer.DialContext(ctx, url.Scheme, url.Host) lat := time.Since(start) if err != nil { if errors.Is(err, net.ErrClosed) || @@ -49,6 +37,7 @@ func (mon *RawHealthMonitor) CheckHealth() (types.HealthCheckResult, error) { } return types.HealthCheckResult{}, err } + defer conn.Close() return types.HealthCheckResult{ Latency: lat, diff --git a/internal/watcher/health/monitor/agent_proxied.go b/internal/watcher/health/monitor/agent_proxied.go deleted file mode 100644 index d9514a70..00000000 --- a/internal/watcher/health/monitor/agent_proxied.go +++ /dev/null @@ -1,71 +0,0 @@ -package monitor - -import ( - "net/url" - - "github.com/yusing/godoxy/internal/agentpool" - "github.com/yusing/godoxy/internal/types" - "github.com/yusing/goutils/synk" -) - -type ( - AgentProxiedMonitor struct { - agent *agentpool.Agent - query synk.Value[string] - *monitor - } - AgentCheckHealthTarget struct { - Scheme string - Host string - Path string - } -) - -func AgentTargetFromURL(url *url.URL) *AgentCheckHealthTarget { - return &AgentCheckHealthTarget{ - Scheme: url.Scheme, - Host: url.Host, - Path: url.Path, - } -} - -func (target *AgentCheckHealthTarget) buildQuery() string { - query := make(url.Values, 3) - query.Set("scheme", target.Scheme) - query.Set("host", target.Host) - query.Set("path", target.Path) - return query.Encode() -} - -func (target *AgentCheckHealthTarget) displayURL() *url.URL { - return &url.URL{ - Scheme: target.Scheme, - Host: target.Host, - Path: target.Path, - } -} - -func NewAgentProxiedMonitor(agent *agentpool.Agent, config types.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor { - mon := &AgentProxiedMonitor{ - agent: agent, - } - mon.monitor = newMonitor(target.displayURL(), config, mon.CheckHealth) - mon.query.Store(target.buildQuery()) - return mon -} - -func (mon *AgentProxiedMonitor) CheckHealth() (types.HealthCheckResult, error) { - resp, err := mon.agent.DoHealthCheck(mon.config.Timeout, mon.query.Load()) - result := types.HealthCheckResult{ - Healthy: resp.Healthy, - Detail: resp.Detail, - Latency: resp.Latency, - } - return result, err -} - -func (mon *AgentProxiedMonitor) UpdateURL(url *url.URL) { - mon.monitor.UpdateURL(url) - newTarget := AgentTargetFromURL(url) - mon.query.Store(newTarget.buildQuery()) -} diff --git a/internal/watcher/health/monitor/docker.go b/internal/watcher/health/monitor/docker.go deleted file mode 100644 index 7e0c7d4e..00000000 --- a/internal/watcher/health/monitor/docker.go +++ /dev/null @@ -1,139 +0,0 @@ -package monitor - -import ( - "net/http" - "net/url" - - "github.com/bytedance/sonic" - "github.com/moby/moby/api/types/container" - "github.com/moby/moby/client" - "github.com/rs/zerolog/log" - "github.com/yusing/godoxy/internal/docker" - "github.com/yusing/godoxy/internal/types" - gperr "github.com/yusing/goutils/errs" - httputils "github.com/yusing/goutils/http" - "github.com/yusing/goutils/task" -) - -type DockerHealthMonitor struct { - *monitor - client *docker.SharedClient - containerID string - fallback types.HealthChecker - - numDockerFailures int -} - -const dockerFailuresThreshold = 3 - -func NewDockerHealthMonitor(client *docker.SharedClient, containerID, alias string, config types.HealthCheckConfig, fallback types.HealthChecker) *DockerHealthMonitor { - mon := new(DockerHealthMonitor) - mon.client = client - mon.containerID = containerID - mon.monitor = newMonitor(fallback.URL(), config, mon.CheckHealth) - mon.fallback = fallback - mon.service = alias - return mon -} - -func (mon *DockerHealthMonitor) Start(parent task.Parent) gperr.Error { - mon.client = mon.client.CloneUnique() - err := mon.monitor.Start(parent) - if err != nil { - return err - } - // zero port - if mon.monitor.task == nil { - return nil - } - mon.client.InterceptHTTPClient(mon.interceptInspectResponse) - mon.monitor.task.OnFinished("close docker client", mon.client.Close) - return nil -} - -func (mon *DockerHealthMonitor) UpdateURL(url *url.URL) { - mon.monitor.UpdateURL(url) - if mon.fallback != nil { - mon.fallback.UpdateURL(url) - } -} - -func (mon *DockerHealthMonitor) interceptInspectResponse(resp *http.Response) (intercepted bool, err error) { - if resp.StatusCode != http.StatusOK { - return false, nil - } - - body, release, err := httputils.ReadAllBody(resp) - resp.Body.Close() - if err != nil { - return false, err - } - - var state container.State - err = sonic.Unmarshal(body, &state) - release(body) - if err != nil { - return false, err - } - - return true, httputils.NewRequestInterceptedError(resp, state) -} - -func (mon *DockerHealthMonitor) CheckHealth() (types.HealthCheckResult, error) { - // if docker health check failed too many times, use fallback forever - if mon.numDockerFailures > dockerFailuresThreshold { - return mon.fallback.CheckHealth() - } - - ctx, cancel := mon.ContextWithTimeout("docker health check timed out") - defer cancel() - - // the actual inspect response is intercepted and returned as RequestInterceptedError - _, err := mon.client.ContainerInspect(ctx, mon.containerID, client.ContainerInspectOptions{}) - - var interceptedErr *httputils.RequestInterceptedError - if !httputils.AsRequestInterceptedError(err, &interceptedErr) { - mon.numDockerFailures++ - log.Debug().Err(err).Str("container_id", mon.containerID).Msg("docker health check failed, using fallback") - return mon.fallback.CheckHealth() - } - - if interceptedErr == nil || interceptedErr.Data == nil { // should not happen - log.Debug().Msgf("intercepted error is nil or data is nil, container_id: %s", mon.containerID) - mon.numDockerFailures++ - log.Debug().Err(err).Str("container_id", mon.containerID).Msg("docker health check failed, using fallback") - return mon.fallback.CheckHealth() - } - - state := interceptedErr.Data.(container.State) - status := state.Status - switch status { - case "dead", "exited", "paused", "restarting", "removing": - mon.numDockerFailures = 0 - return types.HealthCheckResult{ - Healthy: false, - Detail: "container is " + string(status), - }, nil - case "created": - mon.numDockerFailures = 0 - return types.HealthCheckResult{ - Healthy: false, - Detail: "container is not started", - }, nil - } - if state.Health == nil { // no health check from docker, directly use fallback starting from next check - mon.numDockerFailures = dockerFailuresThreshold + 1 - return mon.fallback.CheckHealth() - } - - mon.numDockerFailures = 0 - result := types.HealthCheckResult{ - Healthy: state.Health.Status == container.Healthy, - } - if len(state.Health.Log) > 0 { - lastLog := state.Health.Log[len(state.Health.Log)-1] - result.Detail = lastLog.Output - result.Latency = lastLog.End.Sub(lastLog.Start) - } - return result, nil -} diff --git a/internal/watcher/health/monitor/fileserver.go b/internal/watcher/health/monitor/fileserver.go deleted file mode 100644 index cd1d7568..00000000 --- a/internal/watcher/health/monitor/fileserver.go +++ /dev/null @@ -1,36 +0,0 @@ -package monitor - -import ( - "os" - "time" - - "github.com/yusing/godoxy/internal/types" -) - -type FileServerHealthMonitor struct { - *monitor - path string -} - -func NewFileServerHealthMonitor(config types.HealthCheckConfig, path string) *FileServerHealthMonitor { - mon := &FileServerHealthMonitor{path: path} - mon.monitor = newMonitor(nil, config, mon.CheckHealth) - return mon -} - -func (s *FileServerHealthMonitor) CheckHealth() (types.HealthCheckResult, error) { - start := time.Now() - _, err := os.Stat(s.path) - if err != nil { - if os.IsNotExist(err) { - return types.HealthCheckResult{ - Detail: err.Error(), - }, nil - } - return types.HealthCheckResult{}, err - } - return types.HealthCheckResult{ - Healthy: true, - Latency: time.Since(start), - }, nil -} diff --git a/internal/watcher/health/monitor/monitor.go b/internal/watcher/health/monitor/monitor.go index 91f41bfd..5f56ec40 100644 --- a/internal/watcher/health/monitor/monitor.go +++ b/internal/watcher/health/monitor/monitor.go @@ -21,7 +21,7 @@ import ( ) type ( - HealthCheckFunc func() (result types.HealthCheckResult, err error) + HealthCheckFunc func(url *url.URL) (result types.HealthCheckResult, err error) monitor struct { service string config types.HealthCheckConfig @@ -44,52 +44,56 @@ type ( var ErrNegativeInterval = gperr.New("negative interval") func NewMonitor(r types.Route) types.HealthMonCheck { + target := &r.TargetURL().URL + var mon types.HealthMonCheck if r.IsAgent() { - mon = NewAgentProxiedMonitor(r.GetAgent(), r.HealthCheckConfig(), AgentTargetFromURL(&r.TargetURL().URL)) + mon = NewAgentProxiedMonitor(r.HealthCheckConfig(), r.GetAgent(), target) } else { switch r := r.(type) { case types.ReverseProxyRoute: - mon = NewHTTPHealthMonitor(&r.TargetURL().URL, r.HealthCheckConfig()) + mon = NewHTTPHealthMonitor(r.HealthCheckConfig(), target) case types.FileServerRoute: mon = NewFileServerHealthMonitor(r.HealthCheckConfig(), r.RootPath()) case types.StreamRoute: - mon = NewRawHealthMonitor(&r.TargetURL().URL, r.HealthCheckConfig()) + mon = NewStreamHealthMonitor(r.HealthCheckConfig(), target) default: log.Panic().Msgf("unexpected route type: %T", r) } } if r.IsDocker() { cont := r.ContainerInfo() - client, err := docker.NewClient(cont.DockerCfg) + client, err := docker.NewClient(cont.DockerCfg, true) if err != nil { return mon } r.Task().OnCancel("close_docker_client", client.Close) - return NewDockerHealthMonitor(client, cont.ContainerID, r.Name(), r.HealthCheckConfig(), mon) + + fallback := mon + return NewDockerHealthMonitor(r.HealthCheckConfig(), client, cont.ContainerID, fallback) } return mon } -func newMonitor(u *url.URL, cfg types.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor { +func (mon *monitor) init(u *url.URL, cfg types.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor { if state := config.WorkingState.Load(); state != nil { cfg.ApplyDefaults(state.Value().Defaults.HealthCheck) } else { cfg.ApplyDefaults(types.HealthCheckConfig{}) // use defaults from constants } - mon := &monitor{ - config: cfg, - checkHealth: healthCheckFunc, - startTime: time.Now(), - notifyFunc: notif.Notify, - } - if u == nil { - u = &url.URL{} - } - mon.url.Store(u) + mon.config = cfg + mon.checkHealth = healthCheckFunc + mon.startTime = time.Now() + mon.notifyFunc = notif.Notify mon.status.Store(types.StatusHealthy) mon.lastResult.Store(types.HealthCheckResult{Healthy: true, Detail: "started"}) - return mon + + if u == nil { + mon.url.Store(&url.URL{}) + } else { + mon.url.Store(u) + } + return nil } func (mon *monitor) ContextWithTimeout(cause string) (ctx context.Context, cancel context.CancelFunc) { @@ -104,6 +108,10 @@ func (mon *monitor) ContextWithTimeout(cause string) (ctx context.Context, cance return context.WithTimeoutCause(ctx, mon.config.Timeout, gperr.New(cause)) } +func (mon *monitor) CheckHealth() (types.HealthCheckResult, error) { + return mon.checkHealth(mon.url.Load()) +} + // Start implements task.TaskStarter. func (mon *monitor) Start(parent task.Parent) gperr.Error { if mon.config.Interval <= 0 { @@ -242,7 +250,7 @@ func (mon *monitor) MarshalJSON() ([]byte, error) { func (mon *monitor) checkUpdateHealth() error { logger := log.With().Str("name", mon.Name()).Logger() - result, err := mon.checkHealth() + result, err := mon.checkHealth(mon.url.Load()) var lastStatus types.HealthStatus switch { diff --git a/internal/watcher/health/monitor/monitor_test.go b/internal/watcher/health/monitor/monitor_test.go index db4f1415..ad2d3e06 100644 --- a/internal/watcher/health/monitor/monitor_test.go +++ b/internal/watcher/health/monitor/monitor_test.go @@ -31,7 +31,8 @@ func (t *testNotificationTracker) getStats() (up, down int, last string) { func createTestMonitor(config types.HealthCheckConfig, checkFunc HealthCheckFunc) (*monitor, *testNotificationTracker) { testURL, _ := url.Parse("http://localhost:8080") - mon := newMonitor(testURL, config, checkFunc) + var mon monitor + mon.init(testURL, config, checkFunc) // Override notification functions to track calls instead of actually notifying tracker := &testNotificationTracker{} @@ -52,7 +53,7 @@ func createTestMonitor(config types.HealthCheckConfig, checkFunc HealthCheckFunc } } - return mon, tracker + return &mon, tracker } func TestNotification_ImmediateNotifyAfterZero(t *testing.T) { @@ -62,17 +63,17 @@ func TestNotification_ImmediateNotifyAfterZero(t *testing.T) { Retries: -1, // Immediate notification } - mon, tracker := createTestMonitor(config, func() (types.HealthCheckResult, error) { + mon, tracker := createTestMonitor(config, func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil }) // Start with healthy service - result, err := mon.checkHealth() + result, err := mon.checkHealth(nil) require.NoError(t, err) require.True(t, result.Healthy) // Set to unhealthy - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil } @@ -97,7 +98,7 @@ func TestNotification_WithNotifyAfterThreshold(t *testing.T) { Retries: 2, // Notify after 2 consecutive failures } - mon, tracker := createTestMonitor(config, func() (types.HealthCheckResult, error) { + mon, tracker := createTestMonitor(config, func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil }) @@ -105,7 +106,7 @@ func TestNotification_WithNotifyAfterThreshold(t *testing.T) { mon.status.Store(types.StatusHealthy) // Set to unhealthy - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil } @@ -136,7 +137,7 @@ func TestNotification_ServiceRecoversBeforeThreshold(t *testing.T) { Retries: 3, // Notify after 3 consecutive failures } - mon, tracker := createTestMonitor(config, func() (types.HealthCheckResult, error) { + mon, tracker := createTestMonitor(config, func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil }) @@ -144,7 +145,7 @@ func TestNotification_ServiceRecoversBeforeThreshold(t *testing.T) { mon.status.Store(types.StatusHealthy) // Set to unhealthy - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil } @@ -162,7 +163,7 @@ func TestNotification_ServiceRecoversBeforeThreshold(t *testing.T) { require.Equal(t, 0, up) // Service recovers before third failure - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil } @@ -185,7 +186,7 @@ func TestNotification_ConsecutiveFailureReset(t *testing.T) { Retries: 2, // Notify after 2 consecutive failures } - mon, tracker := createTestMonitor(config, func() (types.HealthCheckResult, error) { + mon, tracker := createTestMonitor(config, func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil }) @@ -193,7 +194,7 @@ func TestNotification_ConsecutiveFailureReset(t *testing.T) { mon.status.Store(types.StatusHealthy) // Set to unhealthy - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil } @@ -202,7 +203,7 @@ func TestNotification_ConsecutiveFailureReset(t *testing.T) { require.NoError(t, err) // Recover briefly - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil } @@ -215,7 +216,7 @@ func TestNotification_ConsecutiveFailureReset(t *testing.T) { require.Equal(t, 1, up) // Go down again - consecutive counter should start from 0 - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil } @@ -246,7 +247,7 @@ func TestNotification_ContextCancellation(t *testing.T) { Retries: 1, } - mon, tracker := createTestMonitor(config, func() (types.HealthCheckResult, error) { + mon, tracker := createTestMonitor(config, func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true}, nil }) @@ -256,7 +257,7 @@ func TestNotification_ContextCancellation(t *testing.T) { // Start healthy, then go unhealthy mon.status.Store(types.StatusHealthy) - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil } @@ -285,7 +286,7 @@ func TestImmediateUpNotification(t *testing.T) { Retries: 2, // NotifyAfter should not affect up notifications } - mon, tracker := createTestMonitor(config, func() (types.HealthCheckResult, error) { + mon, tracker := createTestMonitor(config, func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: false}, nil }) @@ -293,7 +294,7 @@ func TestImmediateUpNotification(t *testing.T) { mon.status.Store(types.StatusUnhealthy) // Set to healthy - mon.checkHealth = func() (types.HealthCheckResult, error) { + mon.checkHealth = func(u *url.URL) (types.HealthCheckResult, error) { return types.HealthCheckResult{Healthy: true, Latency: 50 * time.Millisecond}, nil } diff --git a/internal/watcher/health/monitor/new.go b/internal/watcher/health/monitor/new.go new file mode 100644 index 00000000..9715cbf8 --- /dev/null +++ b/internal/watcher/health/monitor/new.go @@ -0,0 +1,96 @@ +package monitor + +import ( + "fmt" + "net/http" + "net/url" + "time" + + "github.com/rs/zerolog/log" + "github.com/yusing/godoxy/internal/agentpool" + "github.com/yusing/godoxy/internal/docker" + "github.com/yusing/godoxy/internal/types" + healthcheck "github.com/yusing/godoxy/internal/watcher/health/check" +) + +type Result = types.HealthCheckResult +type Monitor = types.HealthMonCheck + +func NewHTTPHealthMonitor(config types.HealthCheckConfig, u *url.URL) Monitor { + var method string + if config.UseGet { + method = http.MethodGet + } else { + method = http.MethodHead + } + + var mon monitor + mon.init(u, config, func(u *url.URL) (result Result, err error) { + if u.Scheme == "h2c" { + return healthcheck.H2C(mon.task.Context(), u, method, config.Path, config.Timeout) + } + return healthcheck.HTTP(u, method, config.Path, config.Timeout) + }) + return &mon +} + +func NewFileServerHealthMonitor(config types.HealthCheckConfig, path string) Monitor { + var mon monitor + mon.init(&url.URL{Scheme: "file", Host: path}, config, func(u *url.URL) (result Result, err error) { + return healthcheck.FileServer(path) + }) + return &mon +} + +func NewStreamHealthMonitor(config types.HealthCheckConfig, targetUrl *url.URL) Monitor { + var mon monitor + mon.init(targetUrl, config, func(u *url.URL) (result Result, err error) { + return healthcheck.Stream(mon.task.Context(), u, config.Timeout) + }) + return &mon +} + +func NewDockerHealthMonitor(config types.HealthCheckConfig, client *docker.SharedClient, containerId string, fallback Monitor) Monitor { + state := healthcheck.NewDockerHealthcheckState(client, containerId) + displayURL := &url.URL{ // only for display purposes, no actual request is made + Scheme: "docker", + Host: client.DaemonHost(), + Path: "/containers/" + containerId + "/json", + } + logger := log.With().Str("host", client.DaemonHost()).Str("container_id", containerId).Logger() + + var mon monitor + mon.init(displayURL, config, func(u *url.URL) (result Result, err error) { + result, err = healthcheck.Docker(mon.task.Context(), state, containerId, config.Timeout) + if err != nil { + logger.Err(err).Msg("docker health check failed, using fallback") + return fallback.CheckHealth() + } + return result, nil + }) + return &mon +} + +func NewAgentProxiedMonitor(config types.HealthCheckConfig, agent *agentpool.Agent, targetUrl *url.URL) Monitor { + var mon monitor + mon.init(targetUrl, config, func(u *url.URL) (result Result, err error) { + return CheckHealthAgentProxied(agent, config.Timeout, targetUrl) + }) + return &mon +} + +func CheckHealthAgentProxied(agent *agentpool.Agent, timeout time.Duration, targetUrl *url.URL) (Result, error) { + query := url.Values{ + "scheme": {targetUrl.Scheme}, + "host": {targetUrl.Host}, + "path": {targetUrl.Path}, + "timeout": {fmt.Sprintf("%d", timeout.Milliseconds())}, + } + resp, err := agent.DoHealthCheck(timeout, query.Encode()) + result := Result{ + Healthy: resp.Healthy, + Detail: resp.Detail, + Latency: resp.Latency, + } + return result, err +}