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]

Reply via email to