adding bootstrapping and config update for notary server

Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
David Lawrence 2016-03-22 21:55:58 -07:00 committed by Riyaz Faizullabhoy
parent 045721250f
commit b8c62731a6
14 changed files with 357 additions and 110 deletions

View File

@ -0,0 +1,17 @@
package main
import (
"fmt"
"github.com/docker/notary/storage"
"golang.org/x/net/context"
)
func bootstrap(ctx context.Context) error {
s := ctx.Value("metaStore")
store, ok := s.(storage.Bootstrapper)
if !ok {
return fmt.Errorf("Store does not support bootstrapping.")
}
return store.Bootstrap()
}

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/dancannon/gorethink"
_ "github.com/docker/distribution/registry/auth/htpasswd"
_ "github.com/docker/distribution/registry/auth/token"
"github.com/docker/go-connections/tlsconfig"
@ -62,34 +63,45 @@ func grpcTLS(configuration *viper.Viper) (*tls.Config, error) {
}
// parses the configuration and returns a backing store for the TUF files
func getStore(configuration *viper.Viper, allowedBackends []string, hRegister healthRegister) (
func getStore(configuration *viper.Viper, hRegister healthRegister) (
storage.MetaStore, error) {
var (
store storage.MetaStore
err error
)
storeConfig, err := utils.ParseStorage(configuration, allowedBackends)
if err != nil {
return nil, err
}
logrus.Infof("Using %s backend", storeConfig.Backend)
backend := configuration.GetString("storage.backend")
logrus.Infof("Using %s backend", backend)
switch storeConfig.Backend {
switch backend {
case notary.MemoryBackend:
return storage.NewMemStorage(), nil
case notary.MySQLBackend:
store, err = storage.NewSQLStorage(storeConfig.Backend, storeConfig.Source)
case notary.MySQLBackend, notary.SQLiteBackend:
storeConfig, err := utils.ParseSQLStorage(configuration)
if err != nil {
return nil, err
}
var s *storage.SQLStorage
s, err = storage.NewSQLStorage(storeConfig.Backend, storeConfig.Source)
store = s
hRegister("DB operational", s.CheckHealth, time.Second*60)
case notary.RethinkDBBackend:
backend := rethinkdb.Connection(storeConfig.CA, storeConfig.Source, storeConfig.Password)
store, err = storage.NewRethinkDBStorage(backend)
var sess *gorethink.Session
storeConfig, err := utils.ParseRethinkDBStorage(configuration)
if err != nil {
return nil, err
}
sess, err = rethinkdb.Connection(storeConfig.CA, storeConfig.Source, storeConfig.AuthKey)
if err == nil {
s := storage.NewRethinkDBStorage(storeConfig.DBName, sess)
store = s
hRegister("DB operational", s.CheckHealth, time.Second*60)
}
default:
err = fmt.Errorf("%s not a supported storage backend", storeConfig.Backend)
err = fmt.Errorf("%s not a supported storage backend", backend)
}
if err != nil {
return nil, fmt.Errorf("Error starting DB driver: %s", err.Error())
return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error())
}
hRegister(
"DB operational", store.CheckHealth, time.Second*60)
return store, nil
}
@ -212,7 +224,7 @@ func parseServerConfig(configFilePath string, hRegister healthRegister) (context
}
ctx = context.WithValue(ctx, "keyAlgorithm", keyAlgo)
store, err := getStore(config, []string{utils.MySQLBackend, utils.MemoryBackend}, hRegister)
store, err := getStore(config, hRegister)
if err != nil {
return nil, server.Config{}, err
}

View File

@ -21,10 +21,11 @@ const (
)
var (
debug bool
logFormat string
configFile string
envPrefix = "NOTARY_SERVER"
debug bool
logFormat string
configFile string
envPrefix = "NOTARY_SERVER"
doBootstrap bool
)
func init() {
@ -32,6 +33,7 @@ func init() {
flag.StringVar(&configFile, "config", "", "Path to configuration file")
flag.BoolVar(&debug, "debug", false, "Enable the debugging server on localhost:8080")
flag.StringVar(&logFormat, "logf", "json", "Set the format of the logs. Only 'json' and 'logfmt' are supported at the moment.")
flag.BoolVar(&doBootstrap, "bootstrap", false, "Do any necessary setup of configured backend storage services")
// this needs to be in init so that _ALL_ logs are in the correct format
if logFormat == jsonLogFormat {
@ -55,8 +57,12 @@ func main() {
logrus.Fatal(err.Error())
}
logrus.Info("Starting Server")
err = server.Run(ctx, serverConfig)
if doBootstrap {
err = bootstrap(ctx)
} else {
logrus.Info("Starting Server")
err = server.Run(ctx, serverConfig)
}
logrus.Error(err.Error())
return

View File

@ -307,7 +307,7 @@ func TestGetStoreInvalid(t *testing.T) {
registerCalled++
}
_, err := getStore(configure(config), []string{"mysql"}, fakeRegister)
_, err := getStore(configure(config), fakeRegister)
require.Error(t, err)
// no health function ever registered
@ -321,14 +321,14 @@ func TestGetStoreDBStore(t *testing.T) {
defer os.Remove(tmpFile.Name())
config := fmt.Sprintf(`{"storage": {"backend": "%s", "db_url": "%s"}}`,
utils.SqliteBackend, tmpFile.Name())
notary.SQLiteBackend, tmpFile.Name())
var registerCalled = 0
var fakeRegister = func(_ string, _ func() error, _ time.Duration) {
registerCalled++
}
store, err := getStore(configure(config), []string{utils.SqliteBackend}, fakeRegister)
store, err := getStore(configure(config), fakeRegister)
require.NoError(t, err)
_, ok := store.(*storage.SQLStorage)
require.True(t, ok)
@ -343,9 +343,8 @@ func TestGetMemoryStore(t *testing.T) {
registerCalled++
}
config := fmt.Sprintf(`{"storage": {"backend": "%s"}}`, utils.MemoryBackend)
store, err := getStore(configure(config),
[]string{utils.MySQLBackend, utils.MemoryBackend}, fakeRegister)
config := fmt.Sprintf(`{"storage": {"backend": "%s"}}`, notary.MemoryBackend)
store, err := getStore(configure(config), fakeRegister)
require.NoError(t, err)
_, ok := store.(*storage.MemStorage)
require.True(t, ok)

View File

@ -17,6 +17,7 @@ import (
"google.golang.org/grpc/credentials"
"github.com/docker/distribution/health"
"github.com/docker/notary"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/passphrase"
"github.com/docker/notary/signer"
@ -74,17 +75,18 @@ func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (p
// mapping
func setUpCryptoservices(configuration *viper.Viper, allowedBackends []string) (
signer.CryptoServiceIndex, error) {
storeConfig, err := utils.ParseStorage(configuration, allowedBackends)
if err != nil {
return nil, err
}
backend := configuration.GetString("storage.backend")
var keyStore trustmanager.KeyStore
if storeConfig.Backend == utils.MemoryBackend {
switch backend {
case notary.MemoryBackend:
keyStore = trustmanager.NewKeyMemoryStore(
passphrase.ConstantRetriever("memory-db-ignore"))
} else {
case notary.MySQLBackend, notary.SQLiteBackend:
storeConfig, err := utils.ParseSQLStorage(configuration)
if err != nil {
return nil, err
}
defaultAlias := configuration.GetString("storage.default_alias")
if defaultAlias == "" {
// backwards compatibility - support this environment variable
@ -207,7 +209,7 @@ func main() {
// setup the cryptoservices
cryptoServices, err := setUpCryptoservices(mainViper,
[]string{utils.MySQLBackend, utils.MemoryBackend})
[]string{notary.MySQLBackend, notary.MemoryBackend})
if err != nil {
logrus.Fatal(err.Error())
}

View File

@ -8,6 +8,7 @@ import (
"os"
"testing"
"github.com/docker/notary"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/keydbstore"
"github.com/docker/notary/tuf/data"
@ -105,8 +106,8 @@ func TestSetupCryptoServicesDBStoreNoDefaultAlias(t *testing.T) {
_, err = setUpCryptoservices(
configure(fmt.Sprintf(
`{"storage": {"backend": "%s", "db_url": "%s"}}`,
utils.SqliteBackend, tmpFile.Name())),
[]string{utils.SqliteBackend})
notary.SqliteBackend, tmpFile.Name())),
[]string{notary.SqliteBackend})
require.Error(t, err)
require.Contains(t, err.Error(), "must provide a default alias for the key DB")
}
@ -136,8 +137,8 @@ func TestSetupCryptoServicesDBStoreSuccess(t *testing.T) {
configure(fmt.Sprintf(
`{"storage": {"backend": "%s", "db_url": "%s"},
"default_alias": "timestamp"}`,
utils.SqliteBackend, tmpFile.Name())),
[]string{utils.SqliteBackend})
notary.SqliteBackend, tmpFile.Name())),
[]string{notary.SqliteBackend})
require.NoError(t, err)
require.Len(t, cryptoServices, 2)
@ -164,9 +165,9 @@ func TestSetupCryptoServicesDBStoreSuccess(t *testing.T) {
// a valid CryptoService is returned.
func TestSetupCryptoServicesMemoryStore(t *testing.T) {
config := configure(fmt.Sprintf(`{"storage": {"backend": "%s"}}`,
utils.MemoryBackend))
notary.MemoryBackend))
cryptoServices, err := setUpCryptoservices(config,
[]string{utils.SqliteBackend, utils.MemoryBackend})
[]string{notary.SqliteBackend, notary.MemoryBackend})
require.NoError(t, err)
require.Len(t, cryptoServices, 2)

View File

@ -49,6 +49,11 @@ const (
// (one year, in seconds, since one year is forever in terms of internet
// content)
CacheMaxAgeLimit = 1 * Year
MySQLBackend = "mysql"
MemoryBackend = "memory"
SQLiteBackend = "sqlite3"
RethinkDBBackend = "rethinkdb"
)
// NotaryDefaultExpiries is the construct used to configure the default expiry times of

View File

@ -1,12 +1,16 @@
package storage
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
"github.com/dancannon/gorethink"
"github.com/docker/notary/storage/rethinkdb"
)
// RDBTUFFile is a tuf file record
type RDBTUFFile struct {
rethinkdb.Timing
Gun string `gorethink:"gun"`
@ -16,14 +20,12 @@ type RDBTUFFile struct {
Data []byte `gorethink:"data"`
}
func (_ RDBTufFile) TableName() string {
// TableName returns the table name for the record type
func (r RDBTUFFile) TableName() string {
return "tuf_files"
}
func (_ RDBTufFile) DatabaseName() string {
return "notaryserver"
}
// RDBKey is the public key record
type RDBKey struct {
rethinkdb.Timing
Gun string `gorethink:"gun"`
@ -32,25 +34,22 @@ type RDBKey struct {
Public []byte `gorethink:"public"`
}
func (_ RDBKey) TableName() string {
// TableName returns the table name for the record type
func (r RDBKey) TableName() string {
return "tuf_keys"
}
func (_ RDBKey) DatabaseName() string {
return "notaryserver"
}
// RethinkDB implements a MetaStore against the Rethink Database
type RethinkDB struct {
dbName string
rdb *gorethink.Session
sess *gorethink.Session
}
// NewRethinkDBStorage initializes a RethinkDB object
func NewRethinkDBStorage(dbName string, sess *gorethink.Session) MetaStore {
func NewRethinkDBStorage(dbName string, sess *gorethink.Session) RethinkDB {
return RethinkDB{
dbName: dbName,
rdb: sess,
sess: sess,
}
}
@ -137,7 +136,7 @@ func (rdb RethinkDB) UpdateMany(gun string, updates []MetaUpdate) error {
// the latest version of the given GUN and role. If there is no data for
// the given GUN and role, an error is returned.
func (rdb RethinkDB) GetCurrent(gun, role string) (created *time.Time, data []byte, err error) {
var file RDBTUFFile
file := RDBTUFFile{}
res, err := gorethink.DB(rdb.dbName).Table(file.TableName()).Get(
RDBTUFFile{
Gun: gun,
@ -147,14 +146,14 @@ func (rdb RethinkDB) GetCurrent(gun, role string) (created *time.Time, data []by
return nil, nil, err
}
defer res.Close()
err = res.One(&key)
err = res.One(&file)
return &file.CreatedAt, file.Data, err
}
// GetChecksum returns the given TUF role file and creation date for the
// GUN with the provided checksum. If the given (gun, role, checksum) are
// not found, it returns storage.ErrNotFound
func (rdb RethinkDB) GetChecksum(gun, tufRole, checksum string) (created *time.Time, data []byte, err error) {
func (rdb RethinkDB) GetChecksum(gun, role, checksum string) (created *time.Time, data []byte, err error) {
var file RDBTUFFile
res, err := gorethink.DB(rdb.dbName).Table(file.TableName()).Get(
RDBTUFFile{
@ -166,7 +165,7 @@ func (rdb RethinkDB) GetChecksum(gun, tufRole, checksum string) (created *time.T
return nil, nil, err
}
defer res.Close()
err = res.One(&key)
err = res.One(&file)
return &file.CreatedAt, file.Data, err
}
@ -180,3 +179,16 @@ func (rdb RethinkDB) Delete(gun string) error {
}
return nil
}
// Bootstrap sets up the database and tables
func (rdb RethinkDB) Bootstrap() error {
return rethinkdb.SetupDB(rdb.sess, rdb.dbName, []rethinkdb.Table{
tufFiles,
keys,
})
}
// CheckHealth is currently a noop
func (rdb RethinkDB) CheckHealth() error {
return nil
}

View File

@ -0,0 +1,21 @@
package storage
import (
"github.com/docker/notary/storage/rethinkdb"
)
var (
tufFiles = rethinkdb.Table{
Name: RDBTUFFile{}.TableName(),
PrimaryKey: []string{"gun", "role", "version"},
SecondaryIndexes: map[string][]string{
"sha256": nil,
},
}
keys = rethinkdb.Table{
Name: RDBKey{}.TableName(),
PrimaryKey: "id",
SecondaryIndexes: nil,
}
)

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"sync"
"time"
"github.com/dancannon/gorethink"
"github.com/docker/notary/passphrase"
@ -39,7 +40,7 @@ func (g RethinkPrivateKey) TableName() string {
return "private_keys"
}
// TableName sets a specific table name for our RethinkPrivateKey
// DatabaseName sets a specific table name for our RethinkPrivateKey
func (g RethinkPrivateKey) DatabaseName() string {
return "notarysigner"
}
@ -76,7 +77,7 @@ func (s *KeyRethinkDBStore) AddKey(keyInfo trustmanager.KeyInfo, privKey data.Pr
now := time.Now()
rethinkPrivKey := RethinkPrivateKey{
rethinkdb.Timing{
Timing: rethinkdb.Timing{
CreatedAt: now,
UpdatedAt: now,
},
@ -112,7 +113,7 @@ func (s *KeyRethinkDBStore) GetKey(name string) (data.PrivateKey, string, error)
}
// Retrieve the RethinkDB private key from the database
var dbPrivateKey RethinkPrivateKey
dbPrivateKey := RethinkPrivateKey{}
res, err := gorethink.DB(dbPrivateKey.DatabaseName()).Table(dbPrivateKey.TableName()).Get(RethinkPrivateKey{KeyID: name}).Run(s.session)
if err != nil {
return nil, "", trustmanager.ErrKeyNotFound{}

8
storage/interface.go Normal file
View File

@ -0,0 +1,8 @@
package storage
// Bootstrapper is a thing that can set itself up
type Bootstrapper interface {
// Bootstrap instructs a configured Bootstrapper to perform
// its setup operations.
Bootstrap() error
}

View File

@ -0,0 +1,137 @@
package rethinkdb
import (
"fmt"
"strings"
"github.com/dancannon/gorethink"
)
func makeDB(session *gorethink.Session, name string) error {
_, err := gorethink.DBCreate(name).RunWrite(session)
if err != nil {
if strings.Contains(err.Error(), "already exists") {
return nil
}
return err
}
resp, err := gorethink.DB(name).Wait().Run(session)
if resp != nil {
resp.Close()
}
return err
}
// Table holds the configuration for setting up a RethinkDB table
type Table struct {
Name string
PrimaryKey interface{}
// Keys are the index names. If len(value) is 0, it is a simple index
// on the field matching the key. Otherwise, it is a compound index
// on the list of fields in the corrensponding slice value.
SecondaryIndexes map[string][]string
}
func (t Table) term(dbName string) gorethink.Term {
return gorethink.DB(dbName).Table(t.Name)
}
func (t Table) wait(session *gorethink.Session, dbName string) error {
resp, err := t.term(dbName).Wait().Run(session)
if resp != nil {
resp.Close()
}
return err
}
func (t Table) create(session *gorethink.Session, dbName string, numReplicas uint) error {
createOpts := gorethink.TableCreateOpts{
PrimaryKey: t.PrimaryKey,
Durability: "hard",
}
if _, err := gorethink.DB(dbName).TableCreate(t.Name, createOpts).RunWrite(session); err != nil {
if !strings.Contains(err.Error(), "already exists") {
return fmt.Errorf("unable to run table creation: %s", err)
}
}
reconfigureOpts := gorethink.ReconfigureOpts{
Shards: 1,
Replicas: numReplicas,
}
if _, err := t.term(dbName).Reconfigure(reconfigureOpts).RunWrite(session); err != nil {
return fmt.Errorf("unable to reconfigure table replication: %s", err)
}
if err := t.wait(session, dbName); err != nil {
return fmt.Errorf("unable to wait for table to be ready after reconfiguring replication: %s", err)
}
for indexName, fieldNames := range t.SecondaryIndexes {
if len(fieldNames) == 0 {
// The field name is the index name.
fieldNames = []string{indexName}
}
if _, err := t.term(dbName).IndexCreateFunc(indexName, func(row gorethink.Term) interface{} {
fields := make([]interface{}, len(fieldNames))
for i, fieldName := range fieldNames {
term := row
for _, subfield := range strings.Split(fieldName, ".") {
term = term.Field(subfield)
}
fields[i] = term
}
if len(fields) == 1 {
return fields[0]
}
return fields
}).RunWrite(session); err != nil {
if !strings.Contains(err.Error(), "already exists") {
return fmt.Errorf("unable to create secondary index %q: %s", indexName, err)
}
}
}
if err := t.wait(session, dbName); err != nil {
return fmt.Errorf("unable to wait for table to be ready after creating secondary indexes: %s", err)
}
return nil
}
// SetupDB hadles creating the database and creating all tables and indexes.
func SetupDB(session *gorethink.Session, dbName string, tables []Table) error {
if err := makeDB(session, dbName); err != nil {
return fmt.Errorf("unable to create database: %s", err)
}
cursor, err := gorethink.DB("rethinkdb").Table("server_config").Count().Run(session)
if err != nil {
return fmt.Errorf("unable to query db server config: %s", err)
}
var replicaCount uint
if err := cursor.One(&replicaCount); err != nil {
return fmt.Errorf("unable to scan db server config count: %s", err)
}
for _, table := range tables {
if err = table.create(session, dbName, replicaCount); err != nil {
return fmt.Errorf("unable to create table %q: %s", table.Name, err)
}
}
return nil
}

View File

@ -13,13 +13,8 @@ import (
"github.com/bugsnag/bugsnag-go"
"github.com/docker/go-connections/tlsconfig"
"github.com/spf13/viper"
)
// Specifies the list of recognized backends
const (
MemoryBackend = "memory"
MySQLBackend = "mysql"
SqliteBackend = "sqlite3"
"github.com/docker/notary"
)
// Storage is a configuration about what storage backend a server should use
@ -28,6 +23,14 @@ type Storage struct {
Source string
}
// RethinkDBStorage is configuration about a RethinkDB backend service
type RethinkDBStorage struct {
Storage
CA string
AuthKey string
DBName string
}
// GetPathRelativeToConfig gets a configuration key which is a path, and if
// it is not empty or an absolute path, returns the absolute path relative
// to the configuration file
@ -81,37 +84,72 @@ func ParseLogLevel(configuration *viper.Viper, defaultLevel logrus.Level) (
return logrus.ParseLevel(logStr)
}
// ParseStorage tries to parse out Storage from a Viper. If backend and
// ParseSQLStorage tries to parse out Storage from a Viper. If backend and
// URL are not provided, returns a nil pointer. Storage is required (if
// a backend is not provided, an error will be returned.)
func ParseStorage(configuration *viper.Viper, allowedBackends []string) (*Storage, error) {
func ParseSQLStorage(configuration *viper.Viper) (*Storage, error) {
store := Storage{
Backend: configuration.GetString("storage.backend"),
Source: configuration.GetString("storage.db_url"),
}
supported := false
store.Backend = strings.ToLower(store.Backend)
for _, backend := range allowedBackends {
if backend == store.Backend {
supported = true
break
}
switch {
case store.Backend != notary.MySQLBackend && store.Backend != notary.SQLiteBackend:
return nil, fmt.Errorf(
"%s is not a supported SQL backend driver",
store.Backend,
)
case store.Source == "":
return nil, fmt.Errorf(
"must provide a non-empty database source for %s",
store.Backend,
)
}
return &store, nil
}
// ParseRethinkDBStorage tries to parse out Storage from a Viper. If backend and
// URL are not provided, returns a nil pointer. Storage is required (if
// a backend is not provided, an error will be returned.)
func ParseRethinkDBStorage(configuration *viper.Viper) (*RethinkDBStorage, error) {
store := RethinkDBStorage{
Storage: Storage{
Backend: configuration.GetString("storage.backend"),
Source: configuration.GetString("storage.db_url"),
},
CA: configuration.GetString("storage.tls_ca_file"),
AuthKey: configuration.GetString("storage.auth_key"),
DBName: configuration.GetString("storage.database"),
}
if !supported {
switch {
case store.Backend != notary.RethinkDBBackend:
return nil, fmt.Errorf(
"must specify one of these supported backends: %s",
strings.Join(allowedBackends, ", "))
"%s is not a supported RethinkDB backend driver",
store.Backend,
)
case store.Source == "":
return nil, fmt.Errorf(
"must provide a non-empty host:port for %s",
store.Backend,
)
case store.CA == "":
return nil, fmt.Errorf(
"cowardly refusal to connect to %s without a CA cert",
store.Backend,
)
case store.AuthKey == "":
return nil, fmt.Errorf(
"cowardly refusal to connect to %s without an AuthKey",
store.Backend,
)
case store.DBName == "":
return nil, fmt.Errorf(
"%s requires a specific database to connect to",
store.Backend,
)
}
if store.Backend == MemoryBackend {
return &Storage{Backend: MemoryBackend}, nil
}
if store.Source == "" {
return nil, fmt.Errorf(
"must provide a non-empty database source for %s", store.Backend)
}
return &store, nil
}

View File

@ -11,6 +11,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/bugsnag/bugsnag-go"
"github.com/docker/notary"
"github.com/docker/notary/trustmanager"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
@ -164,11 +165,10 @@ func TestParseInvalidStorageBackend(t *testing.T) {
`{}`,
}
for _, configJSON := range invalids {
_, err := ParseStorage(configure(configJSON),
[]string{MySQLBackend, SqliteBackend})
_, err := ParseSQLStorage(configure(configJSON))
require.Error(t, err, fmt.Sprintf("'%s' should be an error", configJSON))
require.Contains(t, err.Error(),
"must specify one of these supported backends: mysql, sqlite3")
"is not a supported SQL backend driver")
}
}
@ -178,11 +178,10 @@ func TestParseInvalidStorageNoDBSource(t *testing.T) {
`{"storage": {"backend": "%s"}}`,
`{"storage": {"backend": "%s", "db_url": ""}}`,
}
for _, backend := range []string{MySQLBackend, SqliteBackend} {
for _, backend := range []string{notary.MySQLBackend, notary.SQLiteBackend} {
for _, configJSONFmt := range invalids {
configJSON := fmt.Sprintf(configJSONFmt, backend)
_, err := ParseStorage(configure(configJSON),
[]string{MySQLBackend, SqliteBackend})
_, err := ParseSQLStorage(configure(configJSON))
require.Error(t, err, fmt.Sprintf("'%s' should be an error", configJSON))
require.Contains(t, err.Error(),
fmt.Sprintf("must provide a non-empty database source for %s", backend))
@ -190,22 +189,11 @@ func TestParseInvalidStorageNoDBSource(t *testing.T) {
}
}
// If a memory storage backend is specified, no DB URL is necessary for a
// successful storage parse.
func TestParseStorageMemoryStore(t *testing.T) {
config := configure(`{"storage": {"backend": "MEMORY"}}`)
expected := Storage{Backend: MemoryBackend}
store, err := ParseStorage(config, []string{MySQLBackend, MemoryBackend})
require.NoError(t, err)
require.Equal(t, expected, *store)
}
// A supported backend with DB source will be successfully parsed.
func TestParseStorageDBStore(t *testing.T) {
func TestParseSQLStorageDBStore(t *testing.T) {
config := configure(`{
"storage": {
"backend": "MySQL",
"backend": "mysql",
"db_url": "username:passord@tcp(hostname:1234)/dbname"
}
}`)
@ -215,19 +203,19 @@ func TestParseStorageDBStore(t *testing.T) {
Source: "username:passord@tcp(hostname:1234)/dbname",
}
store, err := ParseStorage(config, []string{"mysql"})
store, err := ParseSQLStorage(config)
require.NoError(t, err)
require.Equal(t, expected, *store)
}
func TestParseStorageWithEnvironmentVariables(t *testing.T) {
func TestParseSQLStorageWithEnvironmentVariables(t *testing.T) {
config := configure(`{
"storage": {
"db_url": "username:passord@tcp(hostname:1234)/dbname"
}
}`)
vars := map[string]string{"STORAGE_BACKEND": "MySQL"}
vars := map[string]string{"STORAGE_BACKEND": "mysql"}
setupEnvironmentVariables(t, vars)
defer cleanupEnvironmentVariables(t, vars)
@ -236,7 +224,7 @@ func TestParseStorageWithEnvironmentVariables(t *testing.T) {
Source: "username:passord@tcp(hostname:1234)/dbname",
}
store, err := ParseStorage(config, []string{"mysql"})
store, err := ParseSQLStorage(config)
require.NoError(t, err)
require.Equal(t, expected, *store)
}