2020-09-15 14:01:26 +00:00
|
|
|
package cassandra
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2021-04-16 21:52:35 +00:00
|
|
|
"net"
|
2020-09-15 14:01:26 +00:00
|
|
|
"os"
|
2021-04-16 21:52:35 +00:00
|
|
|
"path/filepath"
|
2020-09-15 14:01:26 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
2021-02-16 19:39:24 +00:00
|
|
|
|
|
|
|
"github.com/gocql/gocql"
|
|
|
|
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
2020-09-15 14:01:26 +00:00
|
|
|
)
|
|
|
|
|
2021-04-16 21:52:35 +00:00
|
|
|
type containerConfig struct {
|
|
|
|
version string
|
|
|
|
copyFromTo map[string]string
|
|
|
|
sslOpts *gocql.SslOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
type ContainerOpt func(*containerConfig)
|
|
|
|
|
|
|
|
func Version(version string) ContainerOpt {
|
|
|
|
return func(cfg *containerConfig) {
|
|
|
|
cfg.version = version
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func CopyFromTo(copyFromTo map[string]string) ContainerOpt {
|
|
|
|
return func(cfg *containerConfig) {
|
|
|
|
cfg.copyFromTo = copyFromTo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func SslOpts(sslOpts *gocql.SslOptions) ContainerOpt {
|
|
|
|
return func(cfg *containerConfig) {
|
|
|
|
cfg.sslOpts = sslOpts
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Host struct {
|
|
|
|
Name string
|
|
|
|
Port string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h Host) ConnectionURL() string {
|
|
|
|
return net.JoinHostPort(h.Name, h.Port)
|
|
|
|
}
|
|
|
|
|
|
|
|
func PrepareTestContainer(t *testing.T, opts ...ContainerOpt) (Host, func()) {
|
2020-09-15 14:01:26 +00:00
|
|
|
t.Helper()
|
|
|
|
if os.Getenv("CASSANDRA_HOSTS") != "" {
|
2021-04-16 21:52:35 +00:00
|
|
|
host, port, err := net.SplitHostPort(os.Getenv("CASSANDRA_HOSTS"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to split host & port from CASSANDRA_HOSTS (%s): %s", os.Getenv("CASSANDRA_HOSTS"), err)
|
|
|
|
}
|
|
|
|
h := Host{
|
|
|
|
Name: host,
|
|
|
|
Port: port,
|
|
|
|
}
|
|
|
|
return h, func() {}
|
2020-09-15 14:01:26 +00:00
|
|
|
}
|
|
|
|
|
2021-04-16 21:52:35 +00:00
|
|
|
containerCfg := &containerConfig{
|
|
|
|
version: "3.11",
|
2020-09-15 14:01:26 +00:00
|
|
|
}
|
|
|
|
|
2021-04-16 21:52:35 +00:00
|
|
|
for _, opt := range opts {
|
|
|
|
opt(containerCfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
copyFromTo := map[string]string{}
|
|
|
|
for from, to := range containerCfg.copyFromTo {
|
|
|
|
absFrom, err := filepath.Abs(from)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to get absolute path for file %s", from)
|
2020-09-15 14:01:26 +00:00
|
|
|
}
|
2021-04-16 21:52:35 +00:00
|
|
|
copyFromTo[absFrom] = to
|
2020-09-15 14:01:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
runner, err := docker.NewServiceRunner(docker.RunOptions{
|
|
|
|
ImageRepo: "cassandra",
|
2021-04-16 21:52:35 +00:00
|
|
|
ImageTag: containerCfg.version,
|
2020-09-15 14:01:26 +00:00
|
|
|
Ports: []string{"9042/tcp"},
|
|
|
|
CopyFromTo: copyFromTo,
|
|
|
|
Env: []string{"CASSANDRA_BROADCAST_ADDRESS=127.0.0.1"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Could not start docker cassandra: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
svc, err := runner.StartService(context.Background(), func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) {
|
|
|
|
cfg := docker.NewServiceHostPort(host, port)
|
|
|
|
clusterConfig := gocql.NewCluster(cfg.Address())
|
|
|
|
clusterConfig.Authenticator = gocql.PasswordAuthenticator{
|
|
|
|
Username: "cassandra",
|
|
|
|
Password: "cassandra",
|
|
|
|
}
|
|
|
|
clusterConfig.Timeout = 30 * time.Second
|
|
|
|
clusterConfig.ProtoVersion = 4
|
|
|
|
clusterConfig.Port = port
|
|
|
|
|
2021-04-16 21:52:35 +00:00
|
|
|
clusterConfig.SslOpts = containerCfg.sslOpts
|
|
|
|
|
2020-09-15 14:01:26 +00:00
|
|
|
session, err := clusterConfig.CreateSession()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error creating session: %s", err)
|
|
|
|
}
|
|
|
|
defer session.Close()
|
|
|
|
|
|
|
|
// Create keyspace
|
2021-04-16 21:52:35 +00:00
|
|
|
query := session.Query(`CREATE KEYSPACE "vault" WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };`)
|
|
|
|
if err := query.Exec(); err != nil {
|
2020-09-15 14:01:26 +00:00
|
|
|
t.Fatalf("could not create cassandra keyspace: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create table
|
2021-04-16 21:52:35 +00:00
|
|
|
query = session.Query(`CREATE TABLE "vault"."entries" (
|
2020-09-15 14:01:26 +00:00
|
|
|
bucket text,
|
|
|
|
key text,
|
|
|
|
value blob,
|
|
|
|
PRIMARY KEY (bucket, key)
|
|
|
|
) WITH CLUSTERING ORDER BY (key ASC);`)
|
2021-04-16 21:52:35 +00:00
|
|
|
if err := query.Exec(); err != nil {
|
2020-09-15 14:01:26 +00:00
|
|
|
t.Fatalf("could not create cassandra table: %v", err)
|
|
|
|
}
|
|
|
|
return cfg, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Could not start docker cassandra: %s", err)
|
|
|
|
}
|
2021-04-16 21:52:35 +00:00
|
|
|
|
|
|
|
host, port, err := net.SplitHostPort(svc.Config.Address())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to split host & port from address (%s): %s", svc.Config.Address(), err)
|
|
|
|
}
|
|
|
|
h := Host{
|
|
|
|
Name: host,
|
|
|
|
Port: port,
|
|
|
|
}
|
|
|
|
return h, svc.Cleanup
|
2020-09-15 14:01:26 +00:00
|
|
|
}
|