mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
Add ha_enabled for mysql backend (#5122)
* Slight cleanup around mysql ha lock implementation * Removes some duplication around lock table naming * Escapes lock table name with backticks to handle weird characters * Lock table defaults to regular table name + "_lock" * Drop lock table after tests run * Add `ha_enabled` option for mysql storage It defaults to false, and we gate a few things like creating the lock table and preparing lock related statements on it
This commit is contained in:
@@ -37,6 +37,7 @@ const mysqlTLSKey = "default"
|
||||
// within MySQL database.
|
||||
type MySQLBackend struct {
|
||||
dbTable string
|
||||
dbLockTable string
|
||||
client *sql.DB
|
||||
statements map[string]*sql.Stmt
|
||||
logger log.Logger
|
||||
@@ -44,6 +45,7 @@ type MySQLBackend struct {
|
||||
conf map[string]string
|
||||
redirectHost string
|
||||
redirectPort int64
|
||||
haEnabled bool
|
||||
}
|
||||
|
||||
// NewMySQLBackend constructs a MySQL backend using the given API client and
|
||||
@@ -64,7 +66,7 @@ func NewMySQLBackend(conf map[string]string, logger log.Logger) (physical.Backen
|
||||
if !ok {
|
||||
table = "vault"
|
||||
}
|
||||
dbTable := "`" + database + "`" + "." + "`" + table + "`"
|
||||
dbTable := "`" + database + "`.`" + table + "`"
|
||||
|
||||
maxParStr, ok := conf["max_parallel"]
|
||||
var maxParInt int
|
||||
@@ -115,52 +117,72 @@ func NewMySQLBackend(conf map[string]string, logger log.Logger) (physical.Backen
|
||||
}
|
||||
}
|
||||
|
||||
// Default value for ha_enabled
|
||||
haEnabledStr, ok := conf["ha_enabled"]
|
||||
if !ok {
|
||||
haEnabledStr = "false"
|
||||
}
|
||||
haEnabled, err := strconv.ParseBool(haEnabledStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("value [%v] of 'ha_enabled' could not be understood", haEnabledStr)
|
||||
}
|
||||
|
||||
locktable, ok := conf["lock_table"]
|
||||
if !ok {
|
||||
locktable = "vault_lock"
|
||||
locktable = table + "_lock"
|
||||
}
|
||||
|
||||
dbLockTable := database + "." + locktable
|
||||
dbLockTable := "`" + database + "`.`" + locktable + "`"
|
||||
|
||||
// Check table exists
|
||||
var lockTableExist bool
|
||||
lockTableRows, err := db.Query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?", locktable, database)
|
||||
// Only create lock table if ha_enabled is true
|
||||
if haEnabled {
|
||||
// Check table exists
|
||||
var lockTableExist bool
|
||||
lockTableRows, err := db.Query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?", locktable, database)
|
||||
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to check mysql table exist: {{err}}", err)
|
||||
}
|
||||
defer lockTableRows.Close()
|
||||
lockTableExist = lockTableRows.Next()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to check mysql table exist: {{err}}", err)
|
||||
}
|
||||
defer lockTableRows.Close()
|
||||
lockTableExist = lockTableRows.Next()
|
||||
|
||||
// Create the required table if it doesn't exists.
|
||||
if !lockTableExist {
|
||||
create_query := "CREATE TABLE IF NOT EXISTS " + dbLockTable +
|
||||
" (node_job varchar(512), current_leader varchar(512), PRIMARY KEY (node_job))"
|
||||
if _, err := db.Exec(create_query); err != nil {
|
||||
return nil, errwrap.Wrapf("failed to create mysql table: {{err}}", err)
|
||||
// Create the required table if it doesn't exists.
|
||||
if !lockTableExist {
|
||||
create_query := "CREATE TABLE IF NOT EXISTS " + dbLockTable +
|
||||
" (node_job varchar(512), current_leader varchar(512), PRIMARY KEY (node_job))"
|
||||
if _, err := db.Exec(create_query); err != nil {
|
||||
return nil, errwrap.Wrapf("failed to create mysql table: {{err}}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the backend.
|
||||
m := &MySQLBackend{
|
||||
dbTable: dbTable,
|
||||
client: db,
|
||||
statements: make(map[string]*sql.Stmt),
|
||||
logger: logger,
|
||||
permitPool: physical.NewPermitPool(maxParInt),
|
||||
conf: conf,
|
||||
dbTable: dbTable,
|
||||
dbLockTable: dbLockTable,
|
||||
client: db,
|
||||
statements: make(map[string]*sql.Stmt),
|
||||
logger: logger,
|
||||
permitPool: physical.NewPermitPool(maxParInt),
|
||||
conf: conf,
|
||||
haEnabled: haEnabled,
|
||||
}
|
||||
|
||||
// Prepare all the statements required
|
||||
statements := map[string]string{
|
||||
"put": "INSERT INTO " + dbTable +
|
||||
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE vault_value=VALUES(vault_value)",
|
||||
"get": "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
|
||||
"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
|
||||
"list": "SELECT vault_key FROM " + dbTable + " WHERE vault_key LIKE ?",
|
||||
"get_lock": "SELECT current_leader FROM " + dbLockTable + " WHERE node_job = ?",
|
||||
"used_lock": "SELECT IS_USED_LOCK(?)",
|
||||
"get": "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
|
||||
"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
|
||||
"list": "SELECT vault_key FROM " + dbTable + " WHERE vault_key LIKE ?",
|
||||
}
|
||||
|
||||
// Only prepare ha-related statements if we need them
|
||||
if haEnabled {
|
||||
statements["get_lock"] = "SELECT current_leader FROM " + dbLockTable + " WHERE node_job = ?"
|
||||
statements["used_lock"] = "SELECT IS_USED_LOCK(?)"
|
||||
}
|
||||
|
||||
for name, query := range statements {
|
||||
if err := m.prepare(name, query); err != nil {
|
||||
return nil, err
|
||||
@@ -364,7 +386,7 @@ func (m *MySQLBackend) LockWith(key, value string) (physical.Lock, error) {
|
||||
}
|
||||
|
||||
func (m *MySQLBackend) HAEnabled() bool {
|
||||
return true
|
||||
return m.haEnabled
|
||||
}
|
||||
|
||||
// MySQLHALock is a MySQL Lock implementation for the HABackend
|
||||
@@ -549,18 +571,6 @@ func NewMySQLLock(in *MySQLBackend, l log.Logger, key, value string) (*MySQLLock
|
||||
// the rest of the MySQL backend and any cleanup that might need to be done.
|
||||
conn, _ := NewMySQLClient(in.conf, in.logger)
|
||||
|
||||
table, ok := in.conf["lock_table"]
|
||||
if !ok {
|
||||
table = "vault_lock"
|
||||
}
|
||||
|
||||
database, ok := in.conf["database"]
|
||||
if !ok {
|
||||
database = "vault"
|
||||
}
|
||||
|
||||
dbTable := database + "." + table
|
||||
|
||||
m := &MySQLLock{
|
||||
parentConn: in,
|
||||
in: conn,
|
||||
@@ -571,7 +581,7 @@ func NewMySQLLock(in *MySQLBackend, l log.Logger, key, value string) (*MySQLLock
|
||||
}
|
||||
|
||||
statements := map[string]string{
|
||||
"put": "INSERT INTO " + dbTable +
|
||||
"put": "INSERT INTO " + in.dbLockTable +
|
||||
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE current_leader=VALUES(current_leader)",
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestMySQLBackend(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
mysql := b.(*MySQLBackend)
|
||||
_, err := mysql.client.Exec("DROP TABLE " + mysql.dbTable)
|
||||
_, err := mysql.client.Exec("DROP TABLE IF EXISTS " + mysql.dbTable + " ," + mysql.dbLockTable)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to drop table: %v", err)
|
||||
}
|
||||
@@ -80,11 +80,12 @@ func TestMySQLHABackend(t *testing.T) {
|
||||
logger := logging.NewVaultLogger(log.Debug)
|
||||
|
||||
b, err := NewMySQLBackend(map[string]string{
|
||||
"address": address,
|
||||
"database": database,
|
||||
"table": table,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"address": address,
|
||||
"database": database,
|
||||
"table": table,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"ha_enabled": "true",
|
||||
}, logger)
|
||||
|
||||
if err != nil {
|
||||
@@ -93,7 +94,7 @@ func TestMySQLHABackend(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
mysql := b.(*MySQLBackend)
|
||||
_, err := mysql.client.Exec("DROP TABLE " + mysql.dbTable)
|
||||
_, err := mysql.client.Exec("DROP TABLE IF EXISTS " + mysql.dbTable + " ," + mysql.dbLockTable)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to drop table: %v", err)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,17 @@ Additionally, Vault requires the following authentication information.
|
||||
- `password` `(string: <required)` – Specifies the MySQL password to connect to
|
||||
the database.
|
||||
|
||||
### High Availability Parameters
|
||||
|
||||
- `ha_enabled` `(string: "true")` - Specifies if high availability mode is
|
||||
enabled. This is a boolean value, but it is specified as a string like "true"
|
||||
or "false".
|
||||
|
||||
- `lock_table` `(string: "vault_lock")` – Specifies the name of the table to
|
||||
use for storing high availability information. By default, this is the name
|
||||
of the `table` suffixed with `_lock`. If the table does not exist, Vault will
|
||||
attempt to create it.
|
||||
|
||||
## `mysql` Examples
|
||||
|
||||
### Custom Database and Table
|
||||
|
||||
Reference in New Issue
Block a user