diff --git a/CHANGELOG.md b/CHANGELOG.md index 7237cfa..c41367c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ ## Unreleased +FEATURES: + +* Added etcd v2 topology provider implementation (#16) +* Add TopologyController mock for testing improve + +REFACTOR: + +* Refactored docs (add QuickStart doc) and that library base on vhsard router + + + ## 0.0.11 BUG FIXES: diff --git a/Makefile b/Makefile index a0e621e..fd7ef0a 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,8 @@ test/integration: @$(MAKE) -C ./tests/integration test generate/mocks: - mockery --name=Pool --case=underscore --output=mocks/pool --outpkg=mockpool + mockery --name=Pool --case=underscore --output=mocks/pool --outpkg=mockpool # need fix it later + mockery --name=TopologyController --case=underscore --output=mocks/topology --outpkg=mocktopology .PHONY: lint lint: install-lint diff --git a/api.go b/api.go index 1772214..52f87ec 100644 --- a/api.go +++ b/api.go @@ -401,6 +401,7 @@ func (r *Router) RouterMapCallRWImpl( } isVShardRespOk := false + err = future.GetTyped(&[]interface{}{&isVShardRespOk}) if err != nil { cancel() diff --git a/discovery.go b/discovery.go index cd52bf7..cf1ddd9 100644 --- a/discovery.go +++ b/discovery.go @@ -159,6 +159,7 @@ func (r *Router) DiscoveryAllBuckets(ctx context.Context) error { } t := time.Now() + r.log().Info(ctx, "start discovery all buckets") knownBucket := atomic.Int32{} diff --git a/go.mod b/go.mod index b4125b4..89c5ea8 100644 --- a/go.mod +++ b/go.mod @@ -9,14 +9,20 @@ require ( github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 github.com/tarantool/go-tarantool/v2 v2.1.1-0.20240507091106-8b2be0133e9d + github.com/vmihailenco/msgpack/v5 v5.3.5 + go.etcd.io/etcd/client/v2 v2.305.10 golang.org/x/sync v0.6.0 ) require ( + github.com/coreos/go-semver v0.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -28,8 +34,9 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tarantool/go-iproto v1.0.0 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.10 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect diff --git a/go.sum b/go.sum index 777251a..6b6e3ea 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 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= @@ -6,16 +8,24 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -40,7 +50,6 @@ github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -48,7 +57,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -56,14 +64,18 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tarantool/go-iproto v1.0.0 h1:quC4hdFhCuFYaCqOFgUxH2foRkhAy+TlEy7gQLhdVjw= github.com/tarantool/go-iproto v1.0.0/go.mod h1:LNCtdyZxojUed8SbOiYHoc3v9NvaZTB7p96hUySMlIo= -github.com/tarantool/go-tarantool/v2 v2.1.0 h1:IY33WoS8Kqb+TxNnKbzu/7yVkiCNZGhbG5Gw0/tMfSk= -github.com/tarantool/go-tarantool/v2 v2.1.0/go.mod h1:cpjGW5FHAXIMf0PKZte70pMOeadw1MA/hrDv1LblWk4= github.com/tarantool/go-tarantool/v2 v2.1.1-0.20240507091106-8b2be0133e9d h1:L8E9ZtHsGYACsBGU6EtN3fEhLeneVnQ5KwxEeZQk6rk= github.com/tarantool/go-tarantool/v2 v2.1.1-0.20240507091106-8b2be0133e9d/go.mod h1:hjm1qFxll+hs9VoU74MrvwKrXZKv8WGQLhkyeDTHh7Y= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= +go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= +go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= +go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= +go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4= +go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= @@ -80,6 +92,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mocks/topology/topology_controller.go b/mocks/topology/topology_controller.go new file mode 100644 index 0000000..454ce17 --- /dev/null +++ b/mocks/topology/topology_controller.go @@ -0,0 +1,123 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mocktopology + +import ( + context "context" + + uuid "github.com/google/uuid" + mock "github.com/stretchr/testify/mock" + + vshard_router "github.com/KaymeKaydex/go-vshard-router" +) + +// TopologyController is an autogenerated mock type for the TopologyController type +type TopologyController struct { + mock.Mock +} + +// AddInstance provides a mock function with given fields: ctx, rsID, info +func (_m *TopologyController) AddInstance(ctx context.Context, rsID uuid.UUID, info vshard_router.InstanceInfo) error { + ret := _m.Called(ctx, rsID, info) + + if len(ret) == 0 { + panic("no return value specified for AddInstance") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, vshard_router.InstanceInfo) error); ok { + r0 = rf(ctx, rsID, info) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// AddReplicaset provides a mock function with given fields: ctx, rsInfo, instances +func (_m *TopologyController) AddReplicaset(ctx context.Context, rsInfo vshard_router.ReplicasetInfo, instances []vshard_router.InstanceInfo) error { + ret := _m.Called(ctx, rsInfo, instances) + + if len(ret) == 0 { + panic("no return value specified for AddReplicaset") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, vshard_router.ReplicasetInfo, []vshard_router.InstanceInfo) error); ok { + r0 = rf(ctx, rsInfo, instances) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// AddReplicasets provides a mock function with given fields: ctx, replicasets +func (_m *TopologyController) AddReplicasets(ctx context.Context, replicasets map[vshard_router.ReplicasetInfo][]vshard_router.InstanceInfo) error { + ret := _m.Called(ctx, replicasets) + + if len(ret) == 0 { + panic("no return value specified for AddReplicasets") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, map[vshard_router.ReplicasetInfo][]vshard_router.InstanceInfo) error); ok { + r0 = rf(ctx, replicasets) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveInstance provides a mock function with given fields: ctx, rsID, instanceID +func (_m *TopologyController) RemoveInstance(ctx context.Context, rsID uuid.UUID, instanceID uuid.UUID) error { + ret := _m.Called(ctx, rsID, instanceID) + + if len(ret) == 0 { + panic("no return value specified for RemoveInstance") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, uuid.UUID) error); ok { + r0 = rf(ctx, rsID, instanceID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveReplicaset provides a mock function with given fields: ctx, rsID +func (_m *TopologyController) RemoveReplicaset(ctx context.Context, rsID uuid.UUID) []error { + ret := _m.Called(ctx, rsID) + + if len(ret) == 0 { + panic("no return value specified for RemoveReplicaset") + } + + var r0 []error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) []error); ok { + r0 = rf(ctx, rsID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]error) + } + } + + return r0 +} + +// NewTopologyController creates a new instance of TopologyController. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewTopologyController(t interface { + mock.TestingT + Cleanup(func()) +}) *TopologyController { + mock := &TopologyController{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/providers/etcd/doc.go b/providers/etcd/doc.go new file mode 100644 index 0000000..f9f729a --- /dev/null +++ b/providers/etcd/doc.go @@ -0,0 +1,3 @@ +// Package etcd based on moonlibs config library +// https://github.com/moonlibs/config?tab=readme-ov-file#multi-shard-topology-for-custom-sharding-etcdclustermaster +package etcd diff --git a/providers/etcd/provider.go b/providers/etcd/provider.go new file mode 100644 index 0000000..455883c --- /dev/null +++ b/providers/etcd/provider.go @@ -0,0 +1,133 @@ +package etcd + +import ( + "context" + "fmt" + "log" + "path/filepath" + + vshardrouter "github.com/KaymeKaydex/go-vshard-router" + "github.com/google/uuid" + "go.etcd.io/etcd/client/v2" +) + +// Check that provider implements TopologyProvider interface +var _ vshardrouter.TopologyProvider = (*Provider)(nil) + +type Provider struct { + kapi client.KeysAPI + path string +} + +type Config struct { + EtcdConfig client.Config + // Path for storages configuration in etcd for example /project/store/storage + Path string +} + +// NewProvider returns provider to etcd configuration +// Set here path to etcd storages config and etcd config +func NewProvider(cfg Config) *Provider { + c, err := client.New(cfg.EtcdConfig) + if err != nil { + log.Fatal(err) + } + + kapi := client.NewKeysAPI(c) + + return &Provider{ + kapi: kapi, + path: cfg.Path, + } +} + +func (p *Provider) Init(c vshardrouter.TopologyController) error { + resp, err := p.kapi.Get(context.TODO(), p.path, &client.GetOptions{Recursive: true}) + if err != nil { + return err + } + + if resp.Node.Nodes.Len() < 2 { + return fmt.Errorf("etcd path %s subnodes <2; minimum 2 (/clusters & /instances)", p.path) + } + + replicasets := []vshardrouter.ReplicasetInfo{} + instances := map[string][]*vshardrouter.InstanceInfo{} // cluster name to instance info + + for _, node := range resp.Node.Nodes { + switch filepath.Base(node.Key) { + case "clusters": + if len(node.Nodes) < 1 { + return fmt.Errorf("etcd path %s has no clusters", node.Key) + } + + for _, rsNode := range node.Nodes { + replicaset := vshardrouter.ReplicasetInfo{} + + replicaset.Name = filepath.Base(rsNode.Key) + + for _, rsInfoNode := range rsNode.Nodes { + switch filepath.Base(rsInfoNode.Key) { + case "replicaset_uuid": + replicaset.UUID, err = uuid.Parse(rsInfoNode.Value) + if err != nil { + return fmt.Errorf("cant parse replicaset %s uuid %s", replicaset.Name, rsInfoNode.Value) + } + case "master": + // TODO: now we dont support non master auto implementation + default: + continue + } + } + + replicasets = append(replicasets, replicaset) + } + case "instances": + if len(node.Nodes) < 1 { + return fmt.Errorf("etcd path %s has no instances", node.Key) + } + + for _, instanceNode := range node.Nodes { + instance := &vshardrouter.InstanceInfo{} + + for _, instanceInfoNode := range instanceNode.Nodes { + switch filepath.Base(instanceInfoNode.Key) { + case "cluster": + instances[instanceInfoNode.Value] = append(instances[instanceInfoNode.Value], instance) + case "box": + for _, boxNode := range instanceInfoNode.Nodes { + switch filepath.Base(boxNode.Key) { + case "listen": + instance.Addr = boxNode.Value + case "instance_uuid": + instance.UUID, err = uuid.Parse(boxNode.Value) + if err != nil { + return fmt.Errorf("cant parse for instance uuid %s", boxNode.Value) + } + } + } + } + } + } + default: + continue + } + } + + currentTopology := map[vshardrouter.ReplicasetInfo][]vshardrouter.InstanceInfo{} + + for _, replicasetInfo := range replicasets { + var resInst []vshardrouter.InstanceInfo + + for _, inst := range instances[replicasetInfo.Name] { + resInst = append(resInst, *inst) + } + + currentTopology[replicasetInfo] = resInst + } + + return c.AddReplicasets(context.TODO(), currentTopology) +} + +// Close must close connection, but etcd v2 client has no interfaces for this +func (p *Provider) Close() {} diff --git a/providers/etcd/provider_integration_test.go b/providers/etcd/provider_integration_test.go new file mode 100644 index 0000000..372a00e --- /dev/null +++ b/providers/etcd/provider_integration_test.go @@ -0,0 +1,27 @@ +//go:build integration +// +build integration + +package etcd + +import ( + "fmt" + mocktopology "github.com/KaymeKaydex/go-vshard-router/mocks/topology" + "go.etcd.io/etcd/client/v2" + "testing" + "time" +) + +func TestNewProvider(t *testing.T) { + provider := NewProvider(Config{ + EtcdConfig: client.Config{ + Endpoints: []string{"http://127.0.0.1:2379"}, + Transport: client.DefaultTransport, + // set timeout per request to fail fast when the target endpoint is unavailable + HeaderTimeoutPerRequest: time.Second, + }, + Path: "/project/store/storage", + }) + + err := provider.Init(mocktopology.NewTopologyController(t)) + fmt.Println(err) +} diff --git a/providers/static/provider.go b/providers/static/provider.go index 5e24db0..479ba97 100644 --- a/providers/static/provider.go +++ b/providers/static/provider.go @@ -9,6 +9,9 @@ import ( vshardrouter "github.com/KaymeKaydex/go-vshard-router" ) +// Check that provider implements TopologyProvider interface +var _ vshardrouter.TopologyProvider = (*Provider)(nil) + type Provider struct { rs map[vshardrouter.ReplicasetInfo][]vshardrouter.InstanceInfo } diff --git a/providers/viper/provider.go b/providers/viper/provider.go index a9db6d0..4dc3324 100644 --- a/providers/viper/provider.go +++ b/providers/viper/provider.go @@ -12,6 +12,9 @@ import ( vshardrouter "github.com/KaymeKaydex/go-vshard-router" ) +// Check that provider implements TopologyProvider interface +var _ vshardrouter.TopologyProvider = (*Provider)(nil) + type Provider struct { v *srcviper.Viper rs map[vshardrouter.ReplicasetInfo][]vshardrouter.InstanceInfo diff --git a/topology.go b/topology.go index d5fcf8d..aa15dec 100644 --- a/topology.go +++ b/topology.go @@ -52,7 +52,7 @@ func (c *controller) AddInstance(ctx context.Context, rsID uuid.UUID, info Insta return rs.conn.Add(ctx, instance) } -func (c *controller) RemoveInstance(ctx context.Context, rsID, instanceID uuid.UUID) error { +func (c *controller) RemoveInstance(_ context.Context, rsID, instanceID uuid.UUID) error { rs := c.r.idToReplicaset[rsID] if rs == nil { return ErrReplicasetNotExists