This is an automated email from the ASF dual-hosted git repository. shown pushed a commit to branch 1201-yuluo/chore in repository https://gitbox.apache.org/repos/asf/hertzbeat-collector-go.git
commit b060eddb72f388dedc1df2037fd63eb9505f14c9 Author: yuluo-yx <[email protected]> AuthorDate: Mon Dec 1 21:56:57 2025 +0800 feat: add milvus support Signed-off-by: yuluo-yx <[email protected]> --- go.mod | 17 +- go.sum | 358 ++++++++++++ internal/cmd/version/version.go | 5 - internal/collector/basic/ssh/ssh_collector.go | 618 ++++++++++----------- .../common/collect/dispatch/metrics_collector.go | 1 + .../collector/common/types/job/metrics_types.go | 10 + .../collector/extension/milvus/milvus_collector.go | 129 +++++ .../extension/milvus/milvus_collector_test.go | 31 ++ internal/util/crypto/aes_util_test.go | 267 +++++++++ .../{collector/common => util}/ssh/ssh_helper.go | 0 internal/util/ssh/ssh_helper_test.go | 340 ++++++++++++ 11 files changed, 1461 insertions(+), 315 deletions(-) diff --git a/go.mod b/go.mod index b924b9a..7680edb 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,9 @@ require ( github.com/go-sql-driver/mysql v1.9.3 github.com/lib/pq v1.10.9 github.com/microsoft/go-mssqldb v1.9.3 + github.com/milvus-io/milvus-sdk-go/v2 v2.4.2 github.com/prometheus/client_golang v1.23.0 + github.com/prometheus/common v0.65.0 github.com/sijms/go-ora/v2 v2.9.0 github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.10.0 @@ -25,23 +27,36 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.9.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect + github.com/cockroachdb/redact v1.1.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/getsentry/sentry-go v0.12.0 // indirect github.com/goccy/go-json v0.10.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/flatbuffers v23.1.21+incompatible // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/milvus-io/milvus-proto/go-api/v2 v2.4.10-0.20240819025435-512e3b98866a // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/spf13/pflag v1.0.9 // indirect + github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.25.0 // indirect diff --git a/go.sum b/go.sum index b54c994..e5a79fe 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= @@ -12,84 +14,304 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuo github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/apache/arrow/go/v13 v13.0.0 h1:kELrvDQuKZo8csdWYqBQfyi431x6Zs/YJTEgUuSVcWk= github.com/apache/arrow/go/v13 v13.0.0/go.mod h1:W69eByFNO0ZR30q1/7Sr9d83zcVZmF2MiP3fFYAWJOc= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= +github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-faker/faker/v4 v4.1.0 h1:ffuWmpDrducIUOO0QSKSF5Q2dxAht+dhsT9FvVHhPEI= +github.com/go-faker/faker/v4 v4.1.0/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0/FUTTYbhg= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/flatbuffers v23.1.21+incompatible h1:bUqzx/MXCDxuS0hRJL2EfjyZL3uQrPbMocUa8zGqsTA= github.com/google/flatbuffers v23.1.21+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/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/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +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.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 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 v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/microsoft/go-mssqldb v1.9.3 h1:hy4p+LDC8LIGvI3JATnLVmBOLMJbmn5X400mr5j0lPs= github.com/microsoft/go-mssqldb v1.9.3/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA= +github.com/milvus-io/milvus-proto/go-api/v2 v2.4.10-0.20240819025435-512e3b98866a h1:0B/8Fo66D8Aa23Il0yrQvg1KKz92tE/BJ5BvkUxxAAk= +github.com/milvus-io/milvus-proto/go-api/v2 v2.4.10-0.20240819025435-512e3b98866a/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= +github.com/milvus-io/milvus-sdk-go/v2 v2.4.2 h1:Xqf+S7iicElwYoS2Zly8Nf/zKHuZsNy1xQajfdtygVY= +github.com/milvus-io/milvus-sdk-go/v2 v2.4.2/go.mod h1:ulO1YUXKH0PGg50q27grw048GDY9ayB4FPmh7D+FFTA= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= 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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/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= github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sijms/go-ora/v2 v2.9.0 h1:+iQbUeTeCOFMb5BsOMgUhV8KWyrv9yjKpcK4x7+MFrg= github.com/sijms/go-ora/v2 v2.9.0/go.mod h1:QgFInVi3ZWyqAiJwzBQA+nbKYKH77tdp1PYoCqhR2dU= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= @@ -106,43 +328,179 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/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-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +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.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +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= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc/examples v0.0.0-20220617181431-3e7b97febc7f h1:rqzndB2lIQGivcXdTuY3Y9NBvr70X+y77woofSRluec= +google.golang.org/grpc/examples v0.0.0-20220617181431-3e7b97febc7f/go.mod h1:gxndsbNG1n4TZcHGgsYEfVGnTxqfEdfiDv6/DADXX9o= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/cmd/version/version.go b/internal/cmd/version/version.go index 5ea5f85..fd019ce 100644 --- a/internal/cmd/version/version.go +++ b/internal/cmd/version/version.go @@ -17,11 +17,6 @@ * under the License. */ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - package version import ( diff --git a/internal/collector/basic/ssh/ssh_collector.go b/internal/collector/basic/ssh/ssh_collector.go index 79e4370..53ed6ee 100644 --- a/internal/collector/basic/ssh/ssh_collector.go +++ b/internal/collector/basic/ssh/ssh_collector.go @@ -18,372 +18,372 @@ package ssh import ( - "bytes" - "context" - "fmt" - "net" - "strconv" - "strings" - "time" - - sshhelper "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/ssh" - jobtypes "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/job" - consts "hertzbeat.apache.org/hertzbeat-collector-go/internal/constants" - "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/logger" - "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/param" + "bytes" + "context" + "fmt" + "net" + "strconv" + "strings" + "time" + + jobtypes "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/job" + consts "hertzbeat.apache.org/hertzbeat-collector-go/internal/constants" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/logger" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/param" + sshhelper "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/ssh" ) const ( - ProtocolSSH = "ssh" + ProtocolSSH = "ssh" - // Special fields - ResponseTime = "responseTime" - NullValue = consts.NULL_VALUE + // Special fields + ResponseTime = "responseTime" + NullValue = consts.NULL_VALUE - // Parse types - ParseTypeOneRow = "oneRow" // Each line corresponds to one field value, in order of aliasFields - ParseTypeMultiRow = "multiRow" // First line is header with field names, subsequent lines are data rows + // Parse types + ParseTypeOneRow = "oneRow" // Each line corresponds to one field value, in order of aliasFields + ParseTypeMultiRow = "multiRow" // First line is header with field names, subsequent lines are data rows ) // SSHCollector implements SSH collection type SSHCollector struct { - logger logger.Logger + logger logger.Logger } // NewSSHCollector creates a new SSH collector func NewSSHCollector(logger logger.Logger) *SSHCollector { - return &SSHCollector{ - logger: logger.WithName("ssh-collector"), - } + return &SSHCollector{ + logger: logger.WithName("ssh-collector"), + } } // extractSSHConfig extracts SSH configuration from interface{} type // This function uses the parameter replacer for consistent configuration extraction func extractSSHConfig(sshInterface interface{}) (*jobtypes.SSHProtocol, error) { - replacer := param.NewReplacer() - return replacer.ExtractSSHConfig(sshInterface) + replacer := param.NewReplacer() + return replacer.ExtractSSHConfig(sshInterface) } // PreCheck validates the SSH metrics configuration func (jc *SSHCollector) PreCheck(metrics *jobtypes.Metrics) error { - if metrics == nil { - return fmt.Errorf("metrics is nil") - } - - if metrics.SSH == nil { - return fmt.Errorf("SSH protocol configuration is required") - } - - // Extract SSH configuration - sshConfig, err := extractSSHConfig(metrics.SSH) - if err != nil { - return fmt.Errorf("invalid SSH configuration: %w", err) - } - if sshConfig == nil { - return fmt.Errorf("SSH configuration is required") - } - - // Validate required fields - if sshConfig.Host == "" { - return fmt.Errorf("host is required") - } - if sshConfig.Port == "" { - return fmt.Errorf("port is required") - } - if sshConfig.Username == "" { - return fmt.Errorf("username is required") - } - // Either password or private key must be provided for authentication - if sshConfig.Password == "" && sshConfig.PrivateKey == "" { - return fmt.Errorf("either password or privateKey is required for authentication") - } - if sshConfig.Script == "" { - return fmt.Errorf("script is required") - } - // parseScript and timeout are optional, will use defaults if not provided - - return nil + if metrics == nil { + return fmt.Errorf("metrics is nil") + } + + if metrics.SSH == nil { + return fmt.Errorf("SSH protocol configuration is required") + } + + // Extract SSH configuration + sshConfig, err := extractSSHConfig(metrics.SSH) + if err != nil { + return fmt.Errorf("invalid SSH configuration: %w", err) + } + if sshConfig == nil { + return fmt.Errorf("SSH configuration is required") + } + + // Validate required fields + if sshConfig.Host == "" { + return fmt.Errorf("host is required") + } + if sshConfig.Port == "" { + return fmt.Errorf("port is required") + } + if sshConfig.Username == "" { + return fmt.Errorf("username is required") + } + // Either password or private key must be provided for authentication + if sshConfig.Password == "" && sshConfig.PrivateKey == "" { + return fmt.Errorf("either password or privateKey is required for authentication") + } + if sshConfig.Script == "" { + return fmt.Errorf("script is required") + } + // parseScript and timeout are optional, will use defaults if not provided + + return nil } // Collect performs SSH metrics collection func (sshc *SSHCollector) Collect(metrics *jobtypes.Metrics) *jobtypes.CollectRepMetricsData { - startTime := time.Now() - - // Extract SSH configuration - sshConfig, err := extractSSHConfig(metrics.SSH) - if err != nil { - sshc.logger.Error(err, "failed to extract SSH config") - return sshc.createFailResponse(metrics, consts.CollectFail, fmt.Sprintf("SSH config error: %v", err)) - } - if sshConfig == nil { - return sshc.createFailResponse(metrics, consts.CollectFail, "SSH configuration is required") - } - - // Debug level only for collection start - sshc.logger.V(1).Info("starting SSH collection", - "host", sshConfig.Host, - "port", sshConfig.Port) - - // Get timeout - timeout := sshc.getTimeout(sshConfig.Timeout) - - // Execute SSH script - result, err := sshc.executeSSHScript(sshConfig, timeout) - if err != nil { - sshc.logger.Error(err, "failed to execute SSH script") - return sshc.createFailResponse(metrics, consts.CollectUnConnectable, fmt.Sprintf("SSH execution error: %v", err)) - } - - // Calculate response time - responseTime := time.Since(startTime).Milliseconds() - - // Create success response with result - response := sshc.createSuccessResponseWithResult(metrics, result, responseTime) - - return response + startTime := time.Now() + + // Extract SSH configuration + sshConfig, err := extractSSHConfig(metrics.SSH) + if err != nil { + sshc.logger.Error(err, "failed to extract SSH config") + return sshc.createFailResponse(metrics, consts.CollectFail, fmt.Sprintf("SSH config error: %v", err)) + } + if sshConfig == nil { + return sshc.createFailResponse(metrics, consts.CollectFail, "SSH configuration is required") + } + + // Debug level only for collection start + sshc.logger.V(1).Info("starting SSH collection", + "host", sshConfig.Host, + "port", sshConfig.Port) + + // Get timeout + timeout := sshc.getTimeout(sshConfig.Timeout) + + // Execute SSH script + result, err := sshc.executeSSHScript(sshConfig, timeout) + if err != nil { + sshc.logger.Error(err, "failed to execute SSH script") + return sshc.createFailResponse(metrics, consts.CollectUnConnectable, fmt.Sprintf("SSH execution error: %v", err)) + } + + // Calculate response time + responseTime := time.Since(startTime).Milliseconds() + + // Create success response with result + response := sshc.createSuccessResponseWithResult(metrics, result, responseTime) + + return response } // Protocol returns the protocol this collector supports func (sshc *SSHCollector) Protocol() string { - return ProtocolSSH + return ProtocolSSH } // getTimeout parses timeout string and returns duration func (sshc *SSHCollector) getTimeout(timeoutStr string) time.Duration { - if timeoutStr == "" { - return 30 * time.Second // default timeout - } + if timeoutStr == "" { + return 30 * time.Second // default timeout + } - // First try parsing as duration string (e.g., "10s", "5m", "500ms") - if duration, err := time.ParseDuration(timeoutStr); err == nil { - return duration - } + // First try parsing as duration string (e.g., "10s", "5m", "500ms") + if duration, err := time.ParseDuration(timeoutStr); err == nil { + return duration + } - if timeout, err := strconv.Atoi(timeoutStr); err == nil { - return time.Duration(timeout) * time.Millisecond - } + if timeout, err := strconv.Atoi(timeoutStr); err == nil { + return time.Duration(timeout) * time.Millisecond + } - return 30 * time.Second // fallback to default + return 30 * time.Second // fallback to default } // executeSSHScript executes the SSH script and returns the result func (sshc *SSHCollector) executeSSHScript(config *jobtypes.SSHProtocol, timeout time.Duration) (string, error) { - // Create SSH client configuration using helper function - clientConfig, err := sshhelper.CreateSSHClientConfig(config, sshc.logger) - if err != nil { - return "", fmt.Errorf("failed to create SSH client config: %w", err) - } - - // Set timeout for the SSH connection - clientConfig.Timeout = timeout - - // Create context with timeout for the entire operation - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - // Use helper function to dial with context (with proxy support) - conn, err := sshhelper.DialWithProxy(ctx, config, clientConfig, sshc.logger) - if err != nil { - address := net.JoinHostPort(config.Host, config.Port) - return "", fmt.Errorf("failed to connect to SSH server %s: %w", address, err) - } - defer conn.Close() - - // Create a session - session, err := conn.NewSession() - if err != nil { - return "", fmt.Errorf("failed to create SSH session: %w", err) - } - defer session.Close() - - // Execute the script and capture output - var stdout bytes.Buffer - var stderr bytes.Buffer - session.Stdout = &stdout - session.Stderr = &stderr - - // Execute the command - err = session.Run(config.Script) - if err != nil { - stderrStr := stderr.String() - if stderrStr != "" { - return "", fmt.Errorf("script execution failed: %w, stderr: %s", err, stderrStr) - } - return "", fmt.Errorf("script execution failed: %w", err) - } - sshc.logger.Info("SSH script executed successfully") - sshc.logger.Info("SSH script output", "output", stdout.String()) - - return stdout.String(), nil + // Create SSH client configuration using helper function + clientConfig, err := sshhelper.CreateSSHClientConfig(config, sshc.logger) + if err != nil { + return "", fmt.Errorf("failed to create SSH client config: %w", err) + } + + // Set timeout for the SSH connection + clientConfig.Timeout = timeout + + // Create context with timeout for the entire operation + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + // Use helper function to dial with context (with proxy support) + conn, err := sshhelper.DialWithProxy(ctx, config, clientConfig, sshc.logger) + if err != nil { + address := net.JoinHostPort(config.Host, config.Port) + return "", fmt.Errorf("failed to connect to SSH server %s: %w", address, err) + } + defer conn.Close() + + // Create a session + session, err := conn.NewSession() + if err != nil { + return "", fmt.Errorf("failed to create SSH session: %w", err) + } + defer session.Close() + + // Execute the script and capture output + var stdout bytes.Buffer + var stderr bytes.Buffer + session.Stdout = &stdout + session.Stderr = &stderr + + // Execute the command + err = session.Run(config.Script) + if err != nil { + stderrStr := stderr.String() + if stderrStr != "" { + return "", fmt.Errorf("script execution failed: %w, stderr: %s", err, stderrStr) + } + return "", fmt.Errorf("script execution failed: %w", err) + } + sshc.logger.Info("SSH script executed successfully") + sshc.logger.Info("SSH script output", "output", stdout.String()) + + return stdout.String(), nil } // createFailResponse creates a failed metrics data response func (jc *SSHCollector) createFailResponse(metrics *jobtypes.Metrics, code int, message string) *jobtypes.CollectRepMetricsData { - return &jobtypes.CollectRepMetricsData{ - ID: 0, // Will be set by the calling context - MonitorID: 0, // Will be set by the calling context - App: "", // Will be set by the calling context - Metrics: metrics.Name, - Priority: 0, - Time: time.Now().UnixMilli(), - Code: code, - Msg: message, - Fields: make([]jobtypes.Field, 0), - Values: make([]jobtypes.ValueRow, 0), - Labels: make(map[string]string), - Metadata: make(map[string]string), - } + return &jobtypes.CollectRepMetricsData{ + ID: 0, // Will be set by the calling context + MonitorID: 0, // Will be set by the calling context + App: "", // Will be set by the calling context + Metrics: metrics.Name, + Priority: 0, + Time: time.Now().UnixMilli(), + Code: code, + Msg: message, + Fields: make([]jobtypes.Field, 0), + Values: make([]jobtypes.ValueRow, 0), + Labels: make(map[string]string), + Metadata: make(map[string]string), + } } // createSuccessResponseWithResult creates a successful metrics data response with execution result func (sshc *SSHCollector) createSuccessResponseWithResult(metrics *jobtypes.Metrics, result string, responseTime int64) *jobtypes.CollectRepMetricsData { - response := &jobtypes.CollectRepMetricsData{ - ID: 0, // Will be set by the calling context - MonitorID: 0, // Will be set by the calling context - App: "", // Will be set by the calling context - Metrics: metrics.Name, - Priority: 0, - Time: time.Now().UnixMilli(), - Code: consts.CollectSuccess, // Success - Msg: "success", - Fields: make([]jobtypes.Field, 0), - Values: make([]jobtypes.ValueRow, 0), - Labels: make(map[string]string), - Metadata: make(map[string]string), - } - - // Extract SSH configuration to get ParseType - sshConfig, err := extractSSHConfig(metrics.SSH) - if err != nil { - sshc.logger.Error(err, "failed to extract SSH config for parsing") - return response - } - - // Parse response data based on ParseType - if strings.EqualFold(sshConfig.ParseType, ParseTypeOneRow) { - sshc.parseResponseDataByOne(result, metrics.AliasFields, response, responseTime) - } else if strings.EqualFold(sshConfig.ParseType, ParseTypeMultiRow) { - // Default to multi-row format - sshc.parseResponseDataByMulti(result, metrics.AliasFields, response, responseTime) - } - - return response + response := &jobtypes.CollectRepMetricsData{ + ID: 0, // Will be set by the calling context + MonitorID: 0, // Will be set by the calling context + App: "", // Will be set by the calling context + Metrics: metrics.Name, + Priority: 0, + Time: time.Now().UnixMilli(), + Code: consts.CollectSuccess, // Success + Msg: "success", + Fields: make([]jobtypes.Field, 0), + Values: make([]jobtypes.ValueRow, 0), + Labels: make(map[string]string), + Metadata: make(map[string]string), + } + + // Extract SSH configuration to get ParseType + sshConfig, err := extractSSHConfig(metrics.SSH) + if err != nil { + sshc.logger.Error(err, "failed to extract SSH config for parsing") + return response + } + + // Parse response data based on ParseType + if strings.EqualFold(sshConfig.ParseType, ParseTypeOneRow) { + sshc.parseResponseDataByOne(result, metrics.AliasFields, response, responseTime) + } else if strings.EqualFold(sshConfig.ParseType, ParseTypeMultiRow) { + // Default to multi-row format + sshc.parseResponseDataByMulti(result, metrics.AliasFields, response, responseTime) + } + + return response } // parseResponseDataByOne parses SSH script output in single-row format // Each line contains a single field value, matching the order of aliasFields func (sshc *SSHCollector) parseResponseDataByOne(result string, aliasFields []string, response *jobtypes.CollectRepMetricsData, responseTime int64) { - lines := strings.Split(result, "\n") - if len(lines)+1 < len(aliasFields) { - sshc.logger.Error(fmt.Errorf("ssh response data not enough"), "result", result) - return - } - - valueRow := jobtypes.ValueRow{ - Columns: make([]string, 0), - } - - aliasIndex := 0 - lineIndex := 0 - - for aliasIndex < len(aliasFields) { - alias := aliasFields[aliasIndex] - - if strings.EqualFold(alias, ResponseTime) { - // Add response time for special field - valueRow.Columns = append(valueRow.Columns, fmt.Sprintf("%d", responseTime)) - } else { - if lineIndex < len(lines) { - valueRow.Columns = append(valueRow.Columns, strings.TrimSpace(lines[lineIndex])) - } else { - // Add null value if line not available - valueRow.Columns = append(valueRow.Columns, NullValue) - } - lineIndex++ - } - aliasIndex++ - } - - response.Values = append(response.Values, valueRow) - - sshc.logger.Info("Parsed SSH script data (OneRow format)", "aliasFields", aliasFields, "lineCount", len(lines)) - - // Set Fields based on aliasFields - for _, alias := range aliasFields { - field := jobtypes.Field{ - Field: alias, - Type: 1, // Default type - Label: false, - Unit: "", - } - response.Fields = append(response.Fields, field) - } + lines := strings.Split(result, "\n") + if len(lines)+1 < len(aliasFields) { + sshc.logger.Error(fmt.Errorf("ssh response data not enough"), "result", result) + return + } + + valueRow := jobtypes.ValueRow{ + Columns: make([]string, 0), + } + + aliasIndex := 0 + lineIndex := 0 + + for aliasIndex < len(aliasFields) { + alias := aliasFields[aliasIndex] + + if strings.EqualFold(alias, ResponseTime) { + // Add response time for special field + valueRow.Columns = append(valueRow.Columns, fmt.Sprintf("%d", responseTime)) + } else { + if lineIndex < len(lines) { + valueRow.Columns = append(valueRow.Columns, strings.TrimSpace(lines[lineIndex])) + } else { + // Add null value if line not available + valueRow.Columns = append(valueRow.Columns, NullValue) + } + lineIndex++ + } + aliasIndex++ + } + + response.Values = append(response.Values, valueRow) + + sshc.logger.Info("Parsed SSH script data (OneRow format)", "aliasFields", aliasFields, "lineCount", len(lines)) + + // Set Fields based on aliasFields + for _, alias := range aliasFields { + field := jobtypes.Field{ + Field: alias, + Type: 1, // Default type + Label: false, + Unit: "", + } + response.Fields = append(response.Fields, field) + } } // parseResponseDataByMulti parses SSH script output in multi-row format // First line contains field names, subsequent lines contain data func (sshc *SSHCollector) parseResponseDataByMulti(result string, aliasFields []string, response *jobtypes.CollectRepMetricsData, responseTime int64) { - lines := strings.Split(result, "\n") - if len(lines) <= 1 { - sshc.logger.Error(fmt.Errorf("ssh response data only has header"), "result", result) - return - } - - // Parse the first line as field names - fields := strings.Fields(lines[0]) // Use Fields instead of Split to handle multiple spaces - fieldMapping := make(map[string]int, len(fields)) - for i, field := range fields { - fieldMapping[strings.ToLower(strings.TrimSpace(field))] = i - } - - sshc.logger.Info("Parsed SSH script header fields", "fields", fields, "fieldMapping", fieldMapping) - - // Parse subsequent lines as data rows - dataRowCount := 0 - for i := 1; i < len(lines); i++ { - line := strings.TrimSpace(lines[i]) - if line == "" { - continue // Skip empty lines - } - - values := strings.Fields(line) // Use Fields to handle multiple spaces - valueRow := jobtypes.ValueRow{ - Columns: make([]string, 0), - } - - // Build columns based on aliasFields order - for _, alias := range aliasFields { - if strings.EqualFold(alias, ResponseTime) { - // Add response time for special field - valueRow.Columns = append(valueRow.Columns, fmt.Sprintf("%d", responseTime)) - } else { - // Find the field index in the mapping - index, exists := fieldMapping[strings.ToLower(alias)] - if exists && index < len(values) { - valueRow.Columns = append(valueRow.Columns, values[index]) - } else { - // Add null value if field not found or index out of range - valueRow.Columns = append(valueRow.Columns, NullValue) - sshc.logger.V(1).Info("Field not found or index out of range", - "alias", alias, "exists", exists, "index", index, "valuesLength", len(values)) - } - } - } - - response.Values = append(response.Values, valueRow) - dataRowCount++ - } - - sshc.logger.Info("Parsed SSH script data", "dataRows", dataRowCount, "aliasFields", aliasFields) - - // Set Fields based on aliasFields - for _, alias := range aliasFields { - field := jobtypes.Field{ - Field: alias, - Type: 1, // Default type - Label: false, - Unit: "", - } - response.Fields = append(response.Fields, field) - } + lines := strings.Split(result, "\n") + if len(lines) <= 1 { + sshc.logger.Error(fmt.Errorf("ssh response data only has header"), "result", result) + return + } + + // Parse the first line as field names + fields := strings.Fields(lines[0]) // Use Fields instead of Split to handle multiple spaces + fieldMapping := make(map[string]int, len(fields)) + for i, field := range fields { + fieldMapping[strings.ToLower(strings.TrimSpace(field))] = i + } + + sshc.logger.Info("Parsed SSH script header fields", "fields", fields, "fieldMapping", fieldMapping) + + // Parse subsequent lines as data rows + dataRowCount := 0 + for i := 1; i < len(lines); i++ { + line := strings.TrimSpace(lines[i]) + if line == "" { + continue // Skip empty lines + } + + values := strings.Fields(line) // Use Fields to handle multiple spaces + valueRow := jobtypes.ValueRow{ + Columns: make([]string, 0), + } + + // Build columns based on aliasFields order + for _, alias := range aliasFields { + if strings.EqualFold(alias, ResponseTime) { + // Add response time for special field + valueRow.Columns = append(valueRow.Columns, fmt.Sprintf("%d", responseTime)) + } else { + // Find the field index in the mapping + index, exists := fieldMapping[strings.ToLower(alias)] + if exists && index < len(values) { + valueRow.Columns = append(valueRow.Columns, values[index]) + } else { + // Add null value if field not found or index out of range + valueRow.Columns = append(valueRow.Columns, NullValue) + sshc.logger.V(1).Info("Field not found or index out of range", + "alias", alias, "exists", exists, "index", index, "valuesLength", len(values)) + } + } + } + + response.Values = append(response.Values, valueRow) + dataRowCount++ + } + + sshc.logger.Info("Parsed SSH script data", "dataRows", dataRowCount, "aliasFields", aliasFields) + + // Set Fields based on aliasFields + for _, alias := range aliasFields { + field := jobtypes.Field{ + Field: alias, + Type: 1, // Default type + Label: false, + Unit: "", + } + response.Fields = append(response.Fields, field) + } } diff --git a/internal/collector/common/collect/dispatch/metrics_collector.go b/internal/collector/common/collect/dispatch/metrics_collector.go index 131f13d..9b7aff2 100644 --- a/internal/collector/common/collect/dispatch/metrics_collector.go +++ b/internal/collector/common/collect/dispatch/metrics_collector.go @@ -34,6 +34,7 @@ import ( _ "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/basic" "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/collect/strategy" jobtypes "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/job" + _ "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/extension/milvus" "hertzbeat.apache.org/hertzbeat-collector-go/internal/constants" "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/logger" "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/param" diff --git a/internal/collector/common/types/job/metrics_types.go b/internal/collector/common/types/job/metrics_types.go index 293811a..9030a45 100644 --- a/internal/collector/common/types/job/metrics_types.go +++ b/internal/collector/common/types/job/metrics_types.go @@ -51,6 +51,7 @@ type Metrics struct { JMX *JMXProtocol `json:"jmx,omitempty"` Redis *RedisProtocol `json:"redis,omitempty"` MongoDB *MongoDBProtocol `json:"mongodb,omitempty"` + Milvus *MilvusProtocol `json:"milvus,omitempty"` } // Field represents a metric field @@ -278,6 +279,15 @@ type MongoDBProtocol struct { Timeout int `json:"timeout"` } +// MilvusProtocol represents Milvus protocol configuration +type MilvusProtocol struct { + Host string `json:"host"` + Port string `json:"port"` + Username string `json:"username"` + Password string `json:"password"` + Timeout string `json:"timeout"` +} + // GetInterval returns the interval for the metric, using default if not set func (m *Metrics) GetInterval() time.Duration { if m.Interval > 0 { diff --git a/internal/collector/extension/milvus/milvus_collector.go b/internal/collector/extension/milvus/milvus_collector.go new file mode 100644 index 0000000..6f5c0c5 --- /dev/null +++ b/internal/collector/extension/milvus/milvus_collector.go @@ -0,0 +1,129 @@ +package milvus + +import ( + "context" + "fmt" + "strconv" + "strings" + "time" + + "github.com/milvus-io/milvus-sdk-go/v2/client" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/collect/strategy" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/job" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/constants" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/logger" +) + +func init() { + strategy.RegisterFactory(ProtocolMilvus, func(logger logger.Logger) strategy.Collector { + return NewMilvusCollector(logger) + }) +} + +const ( + ProtocolMilvus = "milvus" +) + +type MilvusCollector struct { + logger logger.Logger +} + +func NewMilvusCollector(log logger.Logger) *MilvusCollector { + return &MilvusCollector{ + logger: log.WithName("milvus-collector"), + } +} + +func (c *MilvusCollector) Protocol() string { + return ProtocolMilvus +} + +func (c *MilvusCollector) PreCheck(metrics *job.Metrics) error { + if metrics == nil || metrics.Milvus == nil { + return fmt.Errorf("milvus configuration is missing") + } + return nil +} + +func (c *MilvusCollector) Collect(metrics *job.Metrics) *job.CollectRepMetricsData { + start := time.Now() + milvusConfig := metrics.Milvus + + // 1. Prepare connection + addr := fmt.Sprintf("%s:%s", milvusConfig.Host, milvusConfig.Port) + + timeout := 10 * time.Second + if milvusConfig.Timeout != "" { + if t, err := strconv.Atoi(milvusConfig.Timeout); err == nil { + timeout = time.Duration(t) * time.Millisecond + } + } + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + // 2. Connect + var milvusClient client.Client + var err error + + if milvusConfig.Username != "" && milvusConfig.Password != "" { + milvusClient, err = client.NewDefaultGrpcClientWithAuth(ctx, addr, milvusConfig.Username, milvusConfig.Password) + } else { + milvusClient, err = client.NewDefaultGrpcClient(ctx, addr) + } + + if err != nil { + return c.createFailResponse(metrics, constants.CollectUnConnectable, fmt.Sprintf("failed to connect: %v", err)) + } + defer milvusClient.Close() + + // 3. Collect data + // Currently only supports basic availability check and version retrieval + version, err := milvusClient.GetVersion(ctx) + if err != nil { + return c.createFailResponse(metrics, constants.CollectFail, fmt.Sprintf("failed to get version: %v", err)) + } + + responseTime := time.Since(start).Milliseconds() + + // 4. Parse/Format Response + response := c.createSuccessResponse(metrics) + row := job.ValueRow{Columns: make([]string, len(metrics.AliasFields))} + + for i, field := range metrics.AliasFields { + switch strings.ToLower(field) { + case strings.ToLower(constants.RESPONSE_TIME): + row.Columns[i] = strconv.FormatInt(responseTime, 10) + case "version": + row.Columns[i] = version + case "host": + row.Columns[i] = milvusConfig.Host + case "port": + row.Columns[i] = milvusConfig.Port + default: + row.Columns[i] = constants.NULL_VALUE + } + } + + response.Values = append(response.Values, row) + return response +} + +func (c *MilvusCollector) createSuccessResponse(metrics *job.Metrics) *job.CollectRepMetricsData { + return &job.CollectRepMetricsData{ + Metrics: metrics.Name, + Time: time.Now().UnixMilli(), + Code: constants.CollectSuccess, + Msg: "success", + Values: make([]job.ValueRow, 0), + } +} + +func (c *MilvusCollector) createFailResponse(metrics *job.Metrics, code int, msg string) *job.CollectRepMetricsData { + return &job.CollectRepMetricsData{ + Metrics: metrics.Name, + Time: time.Now().UnixMilli(), + Code: code, + Msg: msg, + } +} diff --git a/internal/collector/extension/milvus/milvus_collector_test.go b/internal/collector/extension/milvus/milvus_collector_test.go new file mode 100644 index 0000000..ae7f2b0 --- /dev/null +++ b/internal/collector/extension/milvus/milvus_collector_test.go @@ -0,0 +1,31 @@ +package milvus + +import ( + "os" + "testing" + + "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/job" + loggertypes "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/logger" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/logger" +) + +func TestMilvusCollector_Collect(t *testing.T) { + metrics := &job.Metrics{ + Name: "milvus_test", + Milvus: &job.MilvusProtocol{ + Host: "localhost", + Port: "19530", + }, + AliasFields: []string{"version", "responseTime", "host", "port"}, + } + + l := logger.DefaultLogger(os.Stdout, loggertypes.LogLevelInfo) + collector := NewMilvusCollector(l) + result := collector.Collect(metrics) + + if result.Code != 0 { + t.Logf("Collect failed: %s", result.Msg) + } else { + t.Logf("Collect success: %+v", result.Values) + } +} diff --git a/internal/util/crypto/aes_util_test.go b/internal/util/crypto/aes_util_test.go new file mode 100644 index 0000000..0ec95a9 --- /dev/null +++ b/internal/util/crypto/aes_util_test.go @@ -0,0 +1,267 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package crypto + +import ( + "bytes" + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAESUtil_SetAndGetSecretKey(t *testing.T) { + util := &AESUtil{} + + // Test setting and getting secret key + testKey := "test-secret-key" + util.SetSecretKey(testKey) + + assert.Equal(t, testKey, util.GetSecretKey()) +} + +func TestAESUtil_GetDefaultAESUtil(t *testing.T) { + // Test singleton pattern + util1 := GetDefaultAESUtil() + util2 := GetDefaultAESUtil() + + // Both should be the same instance + assert.Same(t, util1, util2) +} + +func TestAESUtil_SetDefaultSecretKey(t *testing.T) { + // Test setting default secret key + testKey := "default-secret-key" + SetDefaultSecretKey(testKey) + + // Should be able to retrieve it + assert.Equal(t, testKey, GetDefaultSecretKey()) +} + +func TestAESUtil_AesDecodeWithKey_Success(t *testing.T) { + // Test successful decryption with a valid key + // Using a key that matches the expected format (16 bytes) + key := "1234567890123456" // 16 bytes exactly + + // This test just verifies that the function can be called with valid inputs + // without panicking. Actual decryption testing would require generating + // encrypted data with the exact same algorithm, which is complex to do + // consistently across implementations. + util := &AESUtil{} + + // Test with valid base64 but invalid encrypted data (to avoid requiring actual encryption) + // This will fail decryption but should not panic + validBase64 := "AAAAAAAAAAAAAAAAAAAAAA==" // 16 bytes of zeros, base64 encoded + + _, err := util.AesDecodeWithKey(validBase64, key) + + // Should not fail due to key or base64 issues, but will fail due to invalid encrypted data + // The important thing is that the function processes the inputs correctly + if err != nil { + assert.NotContains(t, err.Error(), "key must be exactly 16 bytes") + assert.NotContains(t, err.Error(), "failed to decode base64") + } +} + +func TestAESUtil_AesDecodeWithKey_InvalidBase64(t *testing.T) { + util := &AESUtil{} + _, err := util.AesDecodeWithKey("invalid-base64!", "1234567890123456") + + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to decode base64") +} + +func TestAESUtil_AesDecodeWithKey_WrongKeyLength(t *testing.T) { + util := &AESUtil{} + _, err := util.AesDecodeWithKey("some-data", "short-key") + + assert.Error(t, err) + assert.Contains(t, err.Error(), "key must be exactly 16 bytes") +} + +func TestAESUtil_AesDecodeWithKey_InvalidEncryptedDataLength(t *testing.T) { + key := "1234567890123456" + + // Data that is not a multiple of AES block size + shortData := base64.StdEncoding.EncodeToString([]byte("short")) + + util := &AESUtil{} + _, err := util.AesDecodeWithKey(shortData, key) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid encrypted data length") +} + +func TestAESUtil_AesDecode_Success(t *testing.T) { + // Test decryption using instance secret key + key := "1234567890123456" + util := &AESUtil{} + util.SetSecretKey(key) + + // Test with valid base64 but invalid encrypted data + validBase64 := "AAAAAAAAAAAAAAAAAAAAAA==" // 16 bytes of zeros, base64 encoded + + _, err := util.AesDecode(validBase64) + + // Should not fail due to missing secret key + if err != nil { + assert.NotContains(t, err.Error(), "AES secret key not set") + } +} + +func TestAESUtil_AesDecode_NoSecretKey(t *testing.T) { + util := &AESUtil{} // No secret key set + + _, err := util.AesDecode("some-data") + + assert.Error(t, err) + assert.Contains(t, err.Error(), "AES secret key not set") +} + +func TestAESUtil_IsCiphertext_Valid(t *testing.T) { + util := &AESUtil{} + util.SetSecretKey("1234567890123456") + + // Valid encrypted data + encryptedData := "TfkdxQxVIy7dGlgXcWgzNA==" // "hello world" encrypted + + assert.True(t, util.IsCiphertext(encryptedData)) +} + +func TestAESUtil_IsCiphertext_InvalidBase64(t *testing.T) { + util := &AESUtil{} + util.SetSecretKey("1234567890123456") + + // Invalid base64 + assert.False(t, util.IsCiphertext("invalid-base64!")) +} + +func TestAESUtil_IsCiphertext_InvalidDecryption(t *testing.T) { + util := &AESUtil{} + util.SetSecretKey("1234567890123456") + + // Valid base64 but invalid encrypted data + invalidEncrypted := base64.StdEncoding.EncodeToString([]byte("invalid-encrypted-data")) + + assert.False(t, util.IsCiphertext(invalidEncrypted)) +} + +func TestConvenienceFunctions(t *testing.T) { + // Test convenience functions that use the default singleton + SetDefaultSecretKey("1234567890123456") + + // Test AesDecode with valid base64 but invalid encrypted data + validBase64 := "AAAAAAAAAAAAAAAAAAAAAA==" // 16 bytes of zeros, base64 encoded + _, err := AesDecode(validBase64) + + // Should not fail due to missing secret key + if err != nil { + assert.NotContains(t, err.Error(), "AES secret key not set") + } + + // Test AesDecodeWithKey with valid base64 but invalid encrypted data + _, err2 := AesDecodeWithKey(validBase64, "1234567890123456") + + // Should not fail due to key issues + if err2 != nil { + assert.NotContains(t, err2.Error(), "key must be exactly 16 bytes") + assert.NotContains(t, err2.Error(), "failed to decode base64") + } + + // Test IsCiphertext with invalid data + assert.False(t, IsCiphertext("invalid-base64")) +} + +func TestRemovePKCS7Padding(t *testing.T) { + tests := []struct { + name string + input []byte + expected []byte + }{ + { + name: "empty data", + input: []byte{}, + expected: []byte{}, + }, + { + name: "data with valid padding", + input: []byte("hello world\x05\x05\x05\x05\x05"), + expected: []byte("hello world"), + }, + { + name: "data with invalid padding length", + input: []byte("hello world\xFF"), + expected: []byte("hello world\xFF"), // Return as-is + }, + { + name: "data with inconsistent padding", + input: []byte("hello world\x03\x03\x05"), + expected: []byte("hello world\x03\x03\x05"), // Return as-is + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := removePKCS7Padding(tt.input) + require.NoError(t, err) + assert.True(t, bytes.Equal(tt.expected, result)) + }) + } +} + +func TestIsPrintableString(t *testing.T) { + tests := []struct { + name string + input string + expected bool + }{ + { + name: "empty string", + input: "", + expected: false, + }, + { + name: "printable ASCII", + input: "hello world", + expected: true, + }, + { + name: "string with space", + input: "hello world 123", + expected: true, + }, + { + name: "string with special characters", + input: "hello-world_123!", + expected: true, + }, + { + name: "string with non-printable character", + input: "hello\x00world", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := isPrintableString(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} \ No newline at end of file diff --git a/internal/collector/common/ssh/ssh_helper.go b/internal/util/ssh/ssh_helper.go similarity index 100% rename from internal/collector/common/ssh/ssh_helper.go rename to internal/util/ssh/ssh_helper.go diff --git a/internal/util/ssh/ssh_helper_test.go b/internal/util/ssh/ssh_helper_test.go new file mode 100644 index 0000000..12e7bfc --- /dev/null +++ b/internal/util/ssh/ssh_helper_test.go @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package ssh + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "io" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" + + jobtypes "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/job" + loggertype "hertzbeat.apache.org/hertzbeat-collector-go/internal/collector/common/types/logger" + "hertzbeat.apache.org/hertzbeat-collector-go/internal/util/logger" +) + +// createTestLogger creates a test logger +func createTestLogger() logger.Logger { + logging := loggertype.DefaultHertzbeatLogging() + return logger.NewLogger(io.Discard, logging) +} + +// generatePrivateKey generates a private key for testing +func generatePrivateKey() (string, error) { + // Generate a new private key + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return "", err + } + + // Marshal the private key to PEM format + privateKeyPEM := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + } + + privateKeyBytes := pem.EncodeToMemory(privateKeyPEM) + return string(privateKeyBytes), nil +} + +func TestCreateSSHClientConfig_PasswordAuth(t *testing.T) { + testLogger := createTestLogger() + + // Test configuration with password authentication + config := &jobtypes.SSHProtocol{ + Username: "testuser", + Password: "testpassword", + } + + clientConfig, err := CreateSSHClientConfig(config, testLogger) + + require.NoError(t, err) + assert.NotNil(t, clientConfig) + assert.Equal(t, "testuser", clientConfig.User) + assert.Len(t, clientConfig.Auth, 1) +} + +func TestCreateSSHClientConfig_PrivateKeyAuth(t *testing.T) { + testLogger := createTestLogger() + + // Generate a private key for testing + privateKeyStr, err := generatePrivateKey() + require.NoError(t, err) + + // Test configuration with private key authentication + config := &jobtypes.SSHProtocol{ + Username: "testuser", + PrivateKey: privateKeyStr, + } + + clientConfig, err := CreateSSHClientConfig(config, testLogger) + + require.NoError(t, err) + assert.NotNil(t, clientConfig) + assert.Equal(t, "testuser", clientConfig.User) + assert.Len(t, clientConfig.Auth, 1) +} + +func TestCreateSSHClientConfig_NoAuthMethod(t *testing.T) { + testLogger := createTestLogger() + + // Test configuration with no authentication method + config := &jobtypes.SSHProtocol{ + Username: "testuser", + } + + _, err := CreateSSHClientConfig(config, testLogger) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "either password or private key must be provided") +} + +func TestParsePrivateKey_RSA(t *testing.T) { + // Generate an RSA private key for testing + privateKeyStr, err := generatePrivateKey() + require.NoError(t, err) + + signer, err := ParsePrivateKey(privateKeyStr) + + require.NoError(t, err) + assert.NotNil(t, signer) + assert.Implements(t, (*ssh.Signer)(nil), signer) +} + +func TestParsePrivateKey_InvalidPEM(t *testing.T) { + invalidKey := "invalid-pem-data" + + _, err := ParsePrivateKey(invalidKey) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to decode PEM block from private key") +} + +func TestParsePrivateKey_UnsupportedType(t *testing.T) { + // Create a PEM block with unsupported type + privateKeyPEM := &pem.Block{ + Type: "UNSUPPORTED PRIVATE KEY", + Bytes: []byte("test-bytes"), + } + + privateKeyStr := string(pem.EncodeToMemory(privateKeyPEM)) + + _, err := ParsePrivateKey(privateKeyStr) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "unsupported private key type") +} + +func TestCreateProxyClientConfig_PasswordAuth(t *testing.T) { + testLogger := createTestLogger() + + // Test configuration with proxy password authentication + config := &jobtypes.SSHProtocol{ + ProxyUsername: "proxyuser", + ProxyPassword: "proxypassword", + } + + clientConfig, err := createProxyClientConfig(config, testLogger) + + require.NoError(t, err) + assert.NotNil(t, clientConfig) + assert.Equal(t, "proxyuser", clientConfig.User) + assert.Len(t, clientConfig.Auth, 1) +} + +func TestCreateProxyClientConfig_PrivateKeyAuth(t *testing.T) { + testLogger := createTestLogger() + + // Generate a private key for testing + privateKeyStr, err := generatePrivateKey() + require.NoError(t, err) + + // Test configuration with proxy private key authentication + config := &jobtypes.SSHProtocol{ + ProxyUsername: "proxyuser", + ProxyPrivateKey: privateKeyStr, + } + + clientConfig, err := createProxyClientConfig(config, testLogger) + + require.NoError(t, err) + assert.NotNil(t, clientConfig) + assert.Equal(t, "proxyuser", clientConfig.User) + assert.Len(t, clientConfig.Auth, 1) +} + +func TestCreateProxyClientConfig_NoAuthMethod(t *testing.T) { + testLogger := createTestLogger() + + // Test configuration with no proxy authentication method + config := &jobtypes.SSHProtocol{ + ProxyUsername: "proxyuser", + } + + _, err := createProxyClientConfig(config, testLogger) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "either proxy password or proxy private key must be provided") +} + +func TestDialWithContext(t *testing.T) { + // This is a unit test - we can't actually dial a connection + // but we can test that the function signature and basic structure is correct + + // Test with cancelled context to verify it handles context properly + ctx, cancel := context.WithCancel(context.Background()) + cancel() // Cancel immediately + + config := &ssh.ClientConfig{ + User: "testuser", + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + // This should fail quickly due to cancelled context + _, err := DialWithContext(ctx, "tcp", "localhost:22", config) + + // We expect an error due to the cancelled context + assert.Error(t, err) +} + +func TestDialWithProxy_Disabled(t *testing.T) { + testLogger := createTestLogger() + + // Test with proxy disabled - should fall back to direct connection + sshConfig := &jobtypes.SSHProtocol{ + Host: "localhost", + Port: "22", + Username: "testuser", + Password: "testpassword", + UseProxy: "false", // Proxy disabled + ProxyHost: "", + ProxyPort: "", + } + + clientConfig := &ssh.ClientConfig{ + User: "testuser", + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + // Test with cancelled context to verify the flow without actually connecting + ctx, cancel := context.WithCancel(context.Background()) + cancel() // Cancel immediately + + _, err := DialWithProxy(ctx, sshConfig, clientConfig, testLogger) + + // We expect an error due to the cancelled context, not due to proxy issues + assert.Error(t, err) +} + +func TestDialWithProxy_EnabledButNotConfigured(t *testing.T) { + testLogger := createTestLogger() + + // Test with proxy enabled but not properly configured + sshConfig := &jobtypes.SSHProtocol{ + Host: "localhost", + Port: "22", + Username: "testuser", + Password: "testpassword", + UseProxy: "true", // Proxy enabled + ProxyHost: "", // But not configured + ProxyPort: "", + } + + clientConfig := &ssh.ClientConfig{ + User: "testuser", + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + // Test with cancelled context to avoid actual connection attempts + ctx, cancel := context.WithCancel(context.Background()) + cancel() // Cancel immediately + + _, err := DialWithProxy(ctx, sshConfig, clientConfig, testLogger) + + // When proxy is enabled but not configured, it should still try to use proxy + // but will fail due to cancelled context rather than configuration issues + assert.Error(t, err) +} + +func TestDialWithProxy_EnabledAndConfigured(t *testing.T) { + testLogger := createTestLogger() + + // Test with proxy enabled and configured but missing credentials + sshConfig := &jobtypes.SSHProtocol{ + Host: "localhost", + Port: "22", + Username: "testuser", + Password: "testpassword", + UseProxy: "true", // Proxy enabled + ProxyHost: "proxyhost", + ProxyPort: "1080", + ProxyUsername: "", // Missing proxy username + } + + clientConfig := &ssh.ClientConfig{ + User: "testuser", + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + ctx := context.Background() + + _, err := DialWithProxy(ctx, sshConfig, clientConfig, testLogger) + + // Should fail due to missing proxy username + assert.Error(t, err) + assert.Contains(t, err.Error(), "proxy username is required when using proxy") +} + +func TestDialWithProxy_EnabledAndFullyConfigured(t *testing.T) { + testLogger := createTestLogger() + + // Test with proxy enabled and fully configured but missing proxy auth + sshConfig := &jobtypes.SSHProtocol{ + Host: "localhost", + Port: "22", + Username: "testuser", + Password: "testpassword", + UseProxy: "true", // Proxy enabled + ProxyHost: "proxyhost", + ProxyPort: "1080", + ProxyUsername: "proxyuser", + // Missing proxy password or private key + } + + clientConfig := &ssh.ClientConfig{ + User: "testuser", + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + ctx := context.Background() + + _, err := DialWithProxy(ctx, sshConfig, clientConfig, testLogger) + + // Should fail due to missing proxy authentication + assert.Error(t, err) + assert.Contains(t, err.Error(), "either proxy password or proxy private key is required when using proxy") +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
