open-vault/physical/cockroachdb/cockroachdb_test.go
Alexander Scheel 8e6e53cf63
Use hashicorp mirror for container pulls (#17778)
When running the test suite in CI (where requests are centralized from
relatively few IPs), we'd occasionally hit Dockerhub's rate limits.
Luckily Hashicorp runs a (limited) public mirror of the containers we
need, so we can switch to them here in the tests.

For consistency between developer and CI, we've opted to have the tests
always pull from the Hashicorp mirror, rather than updating the CI
runner to prefer the mirror.

We exclude nomad and influxdb as we don't presently mirror these repos.

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>

Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-11-02 13:33:17 -04:00

212 lines
5.8 KiB
Go

package cockroachdb
import (
"context"
"database/sql"
"fmt"
"net/url"
"os"
"testing"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/physical"
)
type Config struct {
docker.ServiceURL
TableName string
HATableName string
}
var _ docker.ServiceConfig = &Config{}
func prepareCockroachDBTestContainer(t *testing.T) (func(), *Config) {
if retURL := os.Getenv("CR_URL"); retURL != "" {
s, err := docker.NewServiceURLParse(retURL)
if err != nil {
t.Fatal(err)
}
return func() {}, &Config{
ServiceURL: *s,
TableName: "vault." + defaultTableName,
HATableName: "vault." + defaultHATableName,
}
}
runner, err := docker.NewServiceRunner(docker.RunOptions{
ImageRepo: "docker.mirror.hashicorp.services/cockroachdb/cockroach",
ImageTag: "release-1.0",
ContainerName: "cockroachdb",
Cmd: []string{"start", "--insecure"},
Ports: []string{"26257/tcp"},
})
if err != nil {
t.Fatalf("Could not start docker CockroachDB: %s", err)
}
svc, err := runner.StartService(context.Background(), connectCockroachDB)
if err != nil {
t.Fatalf("Could not start docker CockroachDB: %s", err)
}
return svc.Cleanup, svc.Config.(*Config)
}
func connectCockroachDB(ctx context.Context, host string, port int) (docker.ServiceConfig, error) {
u := url.URL{
Scheme: "postgresql",
User: url.UserPassword("root", ""),
Host: fmt.Sprintf("%s:%d", host, port),
RawQuery: "sslmode=disable",
}
db, err := sql.Open("pgx", u.String())
if err != nil {
return nil, err
}
defer db.Close()
database := "vault"
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", database))
if err != nil {
return nil, err
}
return &Config{
ServiceURL: *docker.NewServiceURL(u),
TableName: database + "." + defaultTableName,
HATableName: database + "." + defaultHATableName,
}, nil
}
func TestCockroachDBBackend(t *testing.T) {
cleanup, config := prepareCockroachDBTestContainer(t)
defer cleanup()
hae := os.Getenv("CR_HA_ENABLED")
if hae == "" {
hae = "true"
}
// Run vault tests
logger := logging.NewVaultLogger(log.Debug)
b1, err := NewCockroachDBBackend(map[string]string{
"connection_url": config.URL().String(),
"table": config.TableName,
"ha_table": config.HATableName,
"ha_enabled": hae,
}, logger)
if err != nil {
t.Fatalf("Failed to create new backend: %v", err)
}
b2, err := NewCockroachDBBackend(map[string]string{
"connection_url": config.URL().String(),
"table": config.TableName,
"ha_table": config.HATableName,
"ha_enabled": hae,
}, logger)
if err != nil {
t.Fatalf("Failed to create new backend: %v", err)
}
defer func() {
truncate(t, b1)
truncate(t, b2)
}()
physical.ExerciseBackend(t, b1)
truncate(t, b1)
physical.ExerciseBackend_ListPrefix(t, b1)
truncate(t, b1)
physical.ExerciseTransactionalBackend(t, b1)
truncate(t, b1)
ha1, ok1 := b1.(physical.HABackend)
ha2, ok2 := b2.(physical.HABackend)
if !ok1 || !ok2 {
t.Fatalf("CockroachDB does not implement HABackend")
}
if ha1.HAEnabled() && ha2.HAEnabled() {
logger.Info("Running ha backend tests")
physical.ExerciseHABackend(t, ha1, ha2)
}
}
func truncate(t *testing.T, b physical.Backend) {
crdb := b.(*CockroachDBBackend)
_, err := crdb.client.Exec("TRUNCATE TABLE " + crdb.table)
if err != nil {
t.Fatalf("Failed to drop table: %v", err)
}
if crdb.haEnabled {
_, err = crdb.client.Exec("TRUNCATE TABLE " + crdb.haTable)
if err != nil {
t.Fatalf("Failed to drop table: %v", err)
}
}
}
func TestValidateDBTable(t *testing.T) {
type testCase struct {
table string
expectErr bool
}
tests := map[string]testCase{
"first character is letter": {"abcdef", false},
"first character is underscore": {"_bcdef", false},
"exclamation point": {"ab!def", true},
"at symbol": {"ab@def", true},
"hash": {"ab#def", true},
"percent": {"ab%def", true},
"carrot": {"ab^def", true},
"ampersand": {"ab&def", true},
"star": {"ab*def", true},
"left paren": {"ab(def", true},
"right paren": {"ab)def", true},
"dash": {"ab-def", true},
"digit": {"a123ef", false},
"dollar end": {"abcde$", false},
"dollar middle": {"ab$def", false},
"dollar start": {"$bcdef", true},
"backtick prefix": {"`bcdef", true},
"backtick middle": {"ab`def", true},
"backtick suffix": {"abcde`", true},
"single quote prefix": {"'bcdef", true},
"single quote middle": {"ab'def", true},
"single quote suffix": {"abcde'", true},
"double quote prefix": {`"bcdef`, true},
"double quote middle": {`ab"def`, true},
"double quote suffix": {`abcde"`, true},
"underscore with all runes": {"_bcd123__a__$", false},
"all runes": {"abcd123__a__$", false},
"default table name": {defaultTableName, false},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
err := validateDBTable(test.table)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
})
t.Run(fmt.Sprintf("database: %s", name), func(t *testing.T) {
dbTable := fmt.Sprintf("%s.%s", test.table, test.table)
err := validateDBTable(dbTable)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
})
}
}