2020-05-15 17:59:37 +03:00
|
|
|
// +build kvdb_etcd
|
|
|
|
|
2020-02-18 21:26:57 +03:00
|
|
|
package etcd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2021-07-06 17:31:24 +03:00
|
|
|
"github.com/btcsuite/btcwallet/walletdb"
|
|
|
|
"github.com/stretchr/testify/require"
|
2021-04-30 12:03:07 +03:00
|
|
|
"go.etcd.io/etcd/clientv3"
|
|
|
|
"go.etcd.io/etcd/clientv3/namespace"
|
2020-02-18 21:26:57 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// testEtcdTimeout is used for all RPC calls initiated by the test fixture.
|
|
|
|
testEtcdTimeout = 5 * time.Second
|
|
|
|
)
|
|
|
|
|
|
|
|
// EtcdTestFixture holds internal state of the etcd test fixture.
|
|
|
|
type EtcdTestFixture struct {
|
|
|
|
t *testing.T
|
|
|
|
cli *clientv3.Client
|
2021-02-09 22:19:31 +03:00
|
|
|
config *Config
|
2020-02-18 21:26:57 +03:00
|
|
|
cleanup func()
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTestEtcdInstance creates an embedded etcd instance for testing, listening
|
|
|
|
// on random open ports. Returns the connection config and a cleanup func that
|
|
|
|
// will stop the etcd instance.
|
2021-02-09 22:19:31 +03:00
|
|
|
func NewTestEtcdInstance(t *testing.T, path string) (*Config, func()) {
|
2020-02-18 21:26:57 +03:00
|
|
|
t.Helper()
|
|
|
|
|
2021-01-06 21:40:30 +03:00
|
|
|
config, cleanup, err := NewEmbeddedEtcdInstance(path, 0, 0)
|
2020-02-18 21:26:57 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error while staring embedded etcd instance: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return config, cleanup
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTestEtcdTestFixture creates a new etcd-test fixture. This is helper
|
|
|
|
// object to facilitate etcd tests and ensure pre and post conditions.
|
|
|
|
func NewEtcdTestFixture(t *testing.T) *EtcdTestFixture {
|
|
|
|
tmpDir, err := ioutil.TempDir("", "etcd")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create temp dir: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
config, etcdCleanup := NewTestEtcdInstance(t, tmpDir)
|
|
|
|
|
|
|
|
cli, err := clientv3.New(clientv3.Config{
|
|
|
|
Endpoints: []string{config.Host},
|
|
|
|
Username: config.User,
|
|
|
|
Password: config.Pass,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
os.RemoveAll(tmpDir)
|
|
|
|
t.Fatalf("unable to create etcd test fixture: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-12-16 20:23:06 +03:00
|
|
|
// Apply the default namespace (since that's what we use in tests).
|
|
|
|
cli.KV = namespace.NewKV(cli.KV, defaultNamespace)
|
|
|
|
cli.Watcher = namespace.NewWatcher(cli.Watcher, defaultNamespace)
|
|
|
|
cli.Lease = namespace.NewLease(cli.Lease, defaultNamespace)
|
|
|
|
|
2020-02-18 21:26:57 +03:00
|
|
|
return &EtcdTestFixture{
|
|
|
|
t: t,
|
|
|
|
cli: cli,
|
|
|
|
config: config,
|
|
|
|
cleanup: func() {
|
|
|
|
etcdCleanup()
|
|
|
|
os.RemoveAll(tmpDir)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-06 17:31:24 +03:00
|
|
|
func (f *EtcdTestFixture) NewBackend() walletdb.DB {
|
|
|
|
db, err := newEtcdBackend(context.TODO(), f.BackendConfig())
|
|
|
|
require.NoError(f.t, err)
|
|
|
|
|
|
|
|
return db
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:26:57 +03:00
|
|
|
// Put puts a string key/value into the test etcd database.
|
|
|
|
func (f *EtcdTestFixture) Put(key, value string) {
|
|
|
|
ctx, cancel := context.WithTimeout(context.TODO(), testEtcdTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
_, err := f.cli.Put(ctx, key, value)
|
|
|
|
if err != nil {
|
|
|
|
f.t.Fatalf("etcd test fixture failed to put: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get queries a key and returns the stored value from the test etcd database.
|
|
|
|
func (f *EtcdTestFixture) Get(key string) string {
|
|
|
|
ctx, cancel := context.WithTimeout(context.TODO(), testEtcdTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
resp, err := f.cli.Get(ctx, key)
|
|
|
|
if err != nil {
|
2020-12-16 20:23:06 +03:00
|
|
|
f.t.Fatalf("etcd test fixture failed to get: %v", err)
|
2020-02-18 21:26:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Kvs) > 0 {
|
|
|
|
return string(resp.Kvs[0].Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dump scans and returns all key/values from the test etcd database.
|
|
|
|
func (f *EtcdTestFixture) Dump() map[string]string {
|
|
|
|
ctx, cancel := context.WithTimeout(context.TODO(), testEtcdTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
2020-12-16 20:23:06 +03:00
|
|
|
resp, err := f.cli.Get(ctx, "\x00", clientv3.WithFromKey())
|
2020-02-18 21:26:57 +03:00
|
|
|
if err != nil {
|
2020-12-16 20:23:06 +03:00
|
|
|
f.t.Fatalf("etcd test fixture failed to get: %v", err)
|
2020-02-18 21:26:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
result := make(map[string]string)
|
|
|
|
for _, kv := range resp.Kvs {
|
|
|
|
result[string(kv.Key)] = string(kv.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// BackendConfig returns the backend config for connecting to theembedded
|
|
|
|
// etcd instance.
|
2021-02-09 22:19:31 +03:00
|
|
|
func (f *EtcdTestFixture) BackendConfig() Config {
|
2020-02-18 21:26:57 +03:00
|
|
|
return *f.config
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cleanup should be called at test fixture teardown to stop the embedded
|
|
|
|
// etcd instance and remove all temp db files form the filesystem.
|
|
|
|
func (f *EtcdTestFixture) Cleanup() {
|
|
|
|
f.cleanup()
|
|
|
|
}
|