feat: registry.
This commit is contained in:
@@ -3,7 +3,6 @@ package kubernetes
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
k8sRegistry "github.com/go-kratos/kratos/contrib/registry/kubernetes/v2"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
|
||||
k8s "k8s.io/client-go/kubernetes"
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// NewRegistry 创建一个注册发现客户端 - Kubernetes
|
||||
func NewRegistry(cfg *conf.Registry) *k8sRegistry.Registry {
|
||||
func NewRegistry(cfg *conf.Registry) *Registry {
|
||||
if cfg == nil || cfg.Kubernetes == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -38,7 +37,7 @@ func NewRegistry(cfg *conf.Registry) *k8sRegistry.Registry {
|
||||
}
|
||||
|
||||
var namespace string
|
||||
reg := k8sRegistry.NewRegistry(clientSet, namespace)
|
||||
reg := New(clientSet, namespace)
|
||||
|
||||
return reg
|
||||
}
|
||||
62
registry/kubernetes/go.mod
Normal file
62
registry/kubernetes/go.mod
Normal file
@@ -0,0 +1,62 @@
|
||||
module github.com/tx7do/kratos-bootstrap/registry/kubernetes
|
||||
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
replace (
|
||||
github.com/armon/go-metrics => github.com/hashicorp/go-metrics v0.4.1
|
||||
|
||||
github.com/tx7do/kratos-bootstrap/api => ../../api
|
||||
github.com/tx7do/kratos-bootstrap/registry => ../registry
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-kratos/kratos/v2 v2.8.4
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tx7do/kratos-bootstrap/api v0.0.20
|
||||
k8s.io/api v0.33.1
|
||||
k8s.io/apimachinery v0.33.1
|
||||
k8s.io/client-go v0.33.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
149
registry/kubernetes/go.sum
Normal file
149
registry/kubernetes/go.sum
Normal file
@@ -0,0 +1,149 @@
|
||||
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/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-kratos/kratos/v2 v2.8.4 h1:eIJLE9Qq9WSoKx+Buy2uPyrahtF/lPh+Xf4MTpxhmjs=
|
||||
github.com/go-kratos/kratos/v2 v2.8.4/go.mod h1:mq62W2101a5uYyRxe+7IdWubu7gZCGYqSNKwGFiiRcw=
|
||||
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-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/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/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
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/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/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw=
|
||||
k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw=
|
||||
k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
|
||||
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4=
|
||||
k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg=
|
||||
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
423
registry/kubernetes/registry.go
Normal file
423
registry/kubernetes/registry.go
Normal file
@@ -0,0 +1,423 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
listerv1 "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/go-kratos/kratos/v2/registry"
|
||||
)
|
||||
|
||||
// Defines the key name of specific fields
|
||||
// Kratos needs to cooperate with the following fields to run properly on Kubernetes:
|
||||
// kratos-service-id: define the ID of the service
|
||||
// kratos-service-app: define the name of the service
|
||||
// kratos-service-version: define the version of the service
|
||||
// kratos-service-metadata: define the metadata of the service
|
||||
// kratos-service-protocols: define the protocols of the service
|
||||
//
|
||||
// Example Deployment:
|
||||
/*
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 5
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
kratos-service-id: "56991810-c77f-4a95-8190-393efa9c1a61"
|
||||
kratos-service-app: "nginx"
|
||||
kratos-service-version: "v3.5.0"
|
||||
annotations:
|
||||
kratos-service-protocols: |
|
||||
{"80": "http"}
|
||||
kratos-service-metadata: |
|
||||
{"region": "sh", "zone": "sh001", "cluster": "pd"}
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
ports:
|
||||
- containerPort: 80
|
||||
*/
|
||||
const (
|
||||
// LabelsKeyServiceID is used to define the ID of the service
|
||||
LabelsKeyServiceID = "kratos-service-id"
|
||||
// LabelsKeyServiceName is used to define the name of the service
|
||||
LabelsKeyServiceName = "kratos-service-app"
|
||||
// LabelsKeyServiceVersion is used to define the version of the service
|
||||
LabelsKeyServiceVersion = "kratos-service-version"
|
||||
// AnnotationsKeyMetadata is used to define the metadata of the service
|
||||
AnnotationsKeyMetadata = "kratos-service-metadata"
|
||||
// AnnotationsKeyProtocolMap is used to define the protocols of the service
|
||||
// Through the value of this field; Kratos can obtain the application layer protocol corresponding to the port
|
||||
// Example value: {"80": "http", "8081": "grpc"}
|
||||
AnnotationsKeyProtocolMap = "kratos-service-protocols"
|
||||
)
|
||||
|
||||
// The Registry simply implements service discovery based on Kubernetes
|
||||
// It has not been verified in the production environment and is currently for reference only
|
||||
type Registry struct {
|
||||
clientSet *kubernetes.Clientset
|
||||
informerFactory informers.SharedInformerFactory
|
||||
podInformer cache.SharedIndexInformer
|
||||
podLister listerv1.PodLister
|
||||
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// New is used to initialize the Registry
|
||||
func New(clientSet *kubernetes.Clientset, namespace string) *Registry {
|
||||
if strings.EqualFold(namespace, "") {
|
||||
namespace = metav1.NamespaceAll
|
||||
}
|
||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(clientSet, time.Minute*10, informers.WithNamespace(namespace))
|
||||
podInformer := informerFactory.Core().V1().Pods().Informer()
|
||||
podLister := informerFactory.Core().V1().Pods().Lister()
|
||||
return &Registry{
|
||||
clientSet: clientSet,
|
||||
informerFactory: informerFactory,
|
||||
podInformer: podInformer,
|
||||
podLister: podLister,
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Register is used to register services
|
||||
// Note that on Kubernetes, it can only be used to update the id/name/version/metadata/protocols of the current service,
|
||||
// but it cannot be used to update node.
|
||||
func (s *Registry) Register(ctx context.Context, service *registry.ServiceInstance) error {
|
||||
// GetMetadata
|
||||
metadataVal, err := marshal(service.Metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate ProtocolMap
|
||||
protocolMap, err := getProtocolMapByEndpoints(service.Endpoints)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
protocolMapVal, err := marshal(protocolMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchBytes, err := jsoniter.Marshal(map[string]any{
|
||||
"metadata": metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
LabelsKeyServiceID: service.ID,
|
||||
LabelsKeyServiceName: service.Name,
|
||||
LabelsKeyServiceVersion: service.Version,
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
AnnotationsKeyMetadata: metadataVal,
|
||||
AnnotationsKeyProtocolMap: protocolMapVal,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = s.clientSet.
|
||||
CoreV1().
|
||||
Pods(GetNamespace()).
|
||||
Patch(ctx, GetPodName(), types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deregister the registration.
|
||||
func (s *Registry) Deregister(ctx context.Context, _ *registry.ServiceInstance) error {
|
||||
return s.Register(ctx, ®istry.ServiceInstance{
|
||||
Metadata: map[string]string{},
|
||||
})
|
||||
}
|
||||
|
||||
// GetService return the service instances in memory according to the service name.
|
||||
func (s *Registry) GetService(_ context.Context, name string) ([]*registry.ServiceInstance, error) {
|
||||
pods, err := s.podLister.List(labels.SelectorFromSet(map[string]string{
|
||||
LabelsKeyServiceName: name,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]*registry.ServiceInstance, 0, len(pods))
|
||||
for _, pod := range pods {
|
||||
if pod.Status.Phase != corev1.PodRunning {
|
||||
continue
|
||||
}
|
||||
instance, err := getServiceInstanceFromPod(pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, instance)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *Registry) sendLatestInstances(ctx context.Context, name string, announcement chan []*registry.ServiceInstance) {
|
||||
instances, err := s.GetService(ctx, name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
announcement <- instances
|
||||
}
|
||||
|
||||
// Watch creates a watcher according to the service name.
|
||||
func (s *Registry) Watch(ctx context.Context, name string) (registry.Watcher, error) {
|
||||
stopCh := make(chan struct{}, 1)
|
||||
announcement := make(chan []*registry.ServiceInstance, 1)
|
||||
_, _ = s.podInformer.AddEventHandler(cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj any) bool {
|
||||
select {
|
||||
case <-stopCh:
|
||||
return false
|
||||
case <-s.stopCh:
|
||||
return false
|
||||
default:
|
||||
pod := obj.(*corev1.Pod)
|
||||
val := pod.GetLabels()[LabelsKeyServiceName]
|
||||
return val == name
|
||||
}
|
||||
},
|
||||
Handler: cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(any) {
|
||||
s.sendLatestInstances(ctx, name, announcement)
|
||||
},
|
||||
UpdateFunc: func(any, any) {
|
||||
s.sendLatestInstances(ctx, name, announcement)
|
||||
},
|
||||
DeleteFunc: func(any) {
|
||||
s.sendLatestInstances(ctx, name, announcement)
|
||||
},
|
||||
},
|
||||
})
|
||||
return NewIterator(announcement, stopCh), nil
|
||||
}
|
||||
|
||||
// Start is used to start the Registry
|
||||
// It is non-blocking
|
||||
func (s *Registry) Start() {
|
||||
s.informerFactory.Start(s.stopCh)
|
||||
if !cache.WaitForCacheSync(s.stopCh, s.podInformer.HasSynced) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Close is used to close the Registry
|
||||
// After closing, any callbacks generated by Watch will not be executed
|
||||
func (s *Registry) Close() {
|
||||
select {
|
||||
case <-s.stopCh:
|
||||
default:
|
||||
close(s.stopCh)
|
||||
}
|
||||
}
|
||||
|
||||
// //////////// K8S Runtime ////////////
|
||||
|
||||
// ServiceAccountNamespacePath defines the location of the namespace file
|
||||
const ServiceAccountNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
||||
|
||||
var currentNamespace = LoadNamespace()
|
||||
|
||||
// LoadNamespace is used to get the current namespace from the file
|
||||
func LoadNamespace() string {
|
||||
data, err := os.ReadFile(ServiceAccountNamespacePath)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// GetNamespace is used to get the namespace of the Pod where the current container is located
|
||||
func GetNamespace() string {
|
||||
return currentNamespace
|
||||
}
|
||||
|
||||
// GetPodName is used to get the name of the Pod where the current container is located
|
||||
func GetPodName() string {
|
||||
return os.Getenv("HOSTNAME")
|
||||
}
|
||||
|
||||
// //////////// ProtocolMap ////////////
|
||||
|
||||
type protocolMap map[string]string
|
||||
|
||||
func (m protocolMap) GetProtocol(port int32) string {
|
||||
return m[strconv.Itoa(int(port))]
|
||||
}
|
||||
|
||||
// //////////// Iterator ////////////
|
||||
|
||||
// Iterator performs the conversion from channel to iterator
|
||||
// It reads the latest changes from the `chan []*registry.ServiceInstance`
|
||||
// And the outside can sense the closure of Iterator through stopCh
|
||||
type Iterator struct {
|
||||
ch chan []*registry.ServiceInstance
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// NewIterator is used to initialize Iterator
|
||||
func NewIterator(channel chan []*registry.ServiceInstance, stopCh chan struct{}) *Iterator {
|
||||
return &Iterator{
|
||||
ch: channel,
|
||||
stopCh: stopCh,
|
||||
}
|
||||
}
|
||||
|
||||
// Next will block until ServiceInstance changes
|
||||
func (iter *Iterator) Next() ([]*registry.ServiceInstance, error) {
|
||||
select {
|
||||
case instances := <-iter.ch:
|
||||
return instances, nil
|
||||
case <-iter.stopCh:
|
||||
return nil, ErrIteratorClosed
|
||||
}
|
||||
}
|
||||
|
||||
// Stop is used to close the iterator
|
||||
func (iter *Iterator) Stop() error {
|
||||
select {
|
||||
case <-iter.stopCh:
|
||||
default:
|
||||
close(iter.stopCh)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// //////////// Helper Func ////////////
|
||||
|
||||
func marshal(in any) (string, error) {
|
||||
return jsoniter.MarshalToString(in)
|
||||
}
|
||||
|
||||
func unmarshal(data string, in any) error {
|
||||
return jsoniter.UnmarshalFromString(data, in)
|
||||
}
|
||||
|
||||
func isEmptyObjectString(s string) bool {
|
||||
switch s {
|
||||
case "", "{}", "null", "nil", "[]":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getProtocolMapByEndpoints(endpoints []string) (protocolMap, error) {
|
||||
ret := protocolMap{}
|
||||
for _, endpoint := range endpoints {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[u.Port()] = u.Scheme
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getProtocolMapFromPod(pod *corev1.Pod) (protocolMap, error) {
|
||||
protoMap := protocolMap{}
|
||||
if s := pod.Annotations[AnnotationsKeyProtocolMap]; !isEmptyObjectString(s) {
|
||||
err := unmarshal(s, &protoMap)
|
||||
if err != nil {
|
||||
return nil, &ErrorHandleResource{Namespace: pod.Namespace, Name: pod.Name, Reason: err}
|
||||
}
|
||||
}
|
||||
return protoMap, nil
|
||||
}
|
||||
|
||||
func getMetadataFromPod(pod *corev1.Pod) (map[string]string, error) {
|
||||
metadata := map[string]string{}
|
||||
if s := pod.Annotations[AnnotationsKeyMetadata]; !isEmptyObjectString(s) {
|
||||
err := unmarshal(s, &metadata)
|
||||
if err != nil {
|
||||
return nil, &ErrorHandleResource{Namespace: pod.Namespace, Name: pod.Name, Reason: err}
|
||||
}
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func getServiceInstanceFromPod(pod *corev1.Pod) (*registry.ServiceInstance, error) {
|
||||
podIP := pod.Status.PodIP
|
||||
podLabels := pod.GetLabels()
|
||||
// Get Metadata
|
||||
metadata, err := getMetadataFromPod(pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get Protocols Definition
|
||||
protocolMap, err := getProtocolMapFromPod(pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get Endpoints
|
||||
var endpoints []string
|
||||
for _, container := range pod.Spec.Containers {
|
||||
for _, cp := range container.Ports {
|
||||
port := cp.ContainerPort
|
||||
protocol := protocolMap.GetProtocol(port)
|
||||
if protocol == "" {
|
||||
if cp.Name != "" {
|
||||
protocol = strings.Split(cp.Name, "-")[0]
|
||||
} else {
|
||||
protocol = string(cp.Protocol)
|
||||
}
|
||||
}
|
||||
addr := fmt.Sprintf("%s://%s:%d", protocol, podIP, port)
|
||||
endpoints = append(endpoints, addr)
|
||||
}
|
||||
}
|
||||
return ®istry.ServiceInstance{
|
||||
ID: podLabels[LabelsKeyServiceID],
|
||||
Name: podLabels[LabelsKeyServiceName],
|
||||
Version: podLabels[LabelsKeyServiceVersion],
|
||||
Metadata: metadata,
|
||||
Endpoints: endpoints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// //////////// Error Definition ////////////
|
||||
|
||||
// ErrIteratorClosed defines the error that the iterator is closed
|
||||
var ErrIteratorClosed = errors.New("iterator closed")
|
||||
|
||||
// ErrorHandleResource defines the error that cannot handle K8S resources normally
|
||||
type ErrorHandleResource struct {
|
||||
Namespace string
|
||||
Name string
|
||||
Reason error
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (err *ErrorHandleResource) Error() string {
|
||||
return fmt.Sprintf("failed to handle resource(namespace=%s, name=%s): %s",
|
||||
err.Namespace, err.Name, err.Reason)
|
||||
}
|
||||
185
registry/kubernetes/registry_test.go
Normal file
185
registry/kubernetes/registry_test.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kratos/kratos/v2/registry"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
namespace = "default"
|
||||
deployName = "hello-deployment"
|
||||
podName = "hello"
|
||||
)
|
||||
|
||||
var deployment = appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: deployName,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: int32Ptr(1),
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app": podName,
|
||||
},
|
||||
},
|
||||
Template: apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"app": podName,
|
||||
},
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx:alpine",
|
||||
Ports: []apiv1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
Protocol: apiv1.ProtocolTCP,
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
Command: []string{
|
||||
"nginx",
|
||||
"-g",
|
||||
"daemon off;",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getClientSet() (*kubernetes.Clientset, error) {
|
||||
restConfig, err := rest.InClusterConfig()
|
||||
home := homedir.HomeDir()
|
||||
|
||||
if err != nil {
|
||||
kubeconfig := filepath.Join(home, ".kube", "config")
|
||||
restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
clientSet, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clientSet, nil
|
||||
}
|
||||
|
||||
func int32Ptr(i int32) *int32 { return &i }
|
||||
|
||||
func TestSetEnv(t *testing.T) {
|
||||
_ = os.Setenv("HOSTNAME", podName)
|
||||
if os.Getenv("HOSTNAME") != podName {
|
||||
t.Fatal("error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistry(t *testing.T) {
|
||||
currentNamespace = "default"
|
||||
|
||||
clientSet, err := getClientSet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := New(clientSet, currentNamespace)
|
||||
r.Start()
|
||||
|
||||
svrHello := ®istry.ServiceInstance{
|
||||
ID: "1",
|
||||
Name: "hello",
|
||||
Version: "v1.0.0",
|
||||
Endpoints: []string{"http://127.0.0.1:80"},
|
||||
}
|
||||
_, err = clientSet.AppsV1().Deployments(namespace).Create(context.Background(), &deployment, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
watch, err := r.Watch(context.Background(), svrHello.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = watch.Stop()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
res, err1 := watch.Next()
|
||||
if err1 != nil {
|
||||
return
|
||||
}
|
||||
t.Logf("watch: %d", len(res))
|
||||
for _, r := range res {
|
||||
t.Logf("next: %+v", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Second)
|
||||
|
||||
pod, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: "app=hello",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(pod.Items) < 1 {
|
||||
t.Fatal("fetch resource error")
|
||||
}
|
||||
|
||||
_ = os.Setenv("HOSTNAME", pod.Items[0].Name)
|
||||
|
||||
// Always remember delete test resource
|
||||
defer func() {
|
||||
_ = clientSet.AppsV1().Deployments(namespace).Delete(context.Background(), deployName, metav1.DeleteOptions{})
|
||||
}()
|
||||
|
||||
if err = r.Register(context.Background(), svrHello); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
res, err := r.GetService(context.Background(), svrHello.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(res) != 1 && res[0].Name != svrHello.Name {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err1 := r.Deregister(context.Background(), svrHello); err1 != nil {
|
||||
t.Fatal(err1)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
res, err = r.GetService(context.Background(), svrHello.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res) != 0 {
|
||||
t.Fatal("not expected empty")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user