mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-25 12:41:10 +01: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:
parent
c3e733623e
commit
801eddf5f8
@ -37,6 +37,7 @@ const mysqlTLSKey = "default"
|
|||||||
// within MySQL database.
|
// within MySQL database.
|
||||||
type MySQLBackend struct {
|
type MySQLBackend struct {
|
||||||
dbTable string
|
dbTable string
|
||||||
|
dbLockTable string
|
||||||
client *sql.DB
|
client *sql.DB
|
||||||
statements map[string]*sql.Stmt
|
statements map[string]*sql.Stmt
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
@ -44,6 +45,7 @@ type MySQLBackend struct {
|
|||||||
conf map[string]string
|
conf map[string]string
|
||||||
redirectHost string
|
redirectHost string
|
||||||
redirectPort int64
|
redirectPort int64
|
||||||
|
haEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMySQLBackend constructs a MySQL backend using the given API client and
|
// 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 {
|
if !ok {
|
||||||
table = "vault"
|
table = "vault"
|
||||||
}
|
}
|
||||||
dbTable := "`" + database + "`" + "." + "`" + table + "`"
|
dbTable := "`" + database + "`.`" + table + "`"
|
||||||
|
|
||||||
maxParStr, ok := conf["max_parallel"]
|
maxParStr, ok := conf["max_parallel"]
|
||||||
var maxParInt int
|
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"]
|
locktable, ok := conf["lock_table"]
|
||||||
if !ok {
|
if !ok {
|
||||||
locktable = "vault_lock"
|
locktable = table + "_lock"
|
||||||
}
|
}
|
||||||
|
|
||||||
dbLockTable := database + "." + locktable
|
dbLockTable := "`" + database + "`.`" + locktable + "`"
|
||||||
|
|
||||||
// Check table exists
|
// Only create lock table if ha_enabled is true
|
||||||
var lockTableExist bool
|
if haEnabled {
|
||||||
lockTableRows, err := db.Query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?", locktable, database)
|
// 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 {
|
if err != nil {
|
||||||
return nil, errwrap.Wrapf("failed to check mysql table exist: {{err}}", err)
|
return nil, errwrap.Wrapf("failed to check mysql table exist: {{err}}", err)
|
||||||
}
|
}
|
||||||
defer lockTableRows.Close()
|
defer lockTableRows.Close()
|
||||||
lockTableExist = lockTableRows.Next()
|
lockTableExist = lockTableRows.Next()
|
||||||
|
|
||||||
// Create the required table if it doesn't exists.
|
// Create the required table if it doesn't exists.
|
||||||
if !lockTableExist {
|
if !lockTableExist {
|
||||||
create_query := "CREATE TABLE IF NOT EXISTS " + dbLockTable +
|
create_query := "CREATE TABLE IF NOT EXISTS " + dbLockTable +
|
||||||
" (node_job varchar(512), current_leader varchar(512), PRIMARY KEY (node_job))"
|
" (node_job varchar(512), current_leader varchar(512), PRIMARY KEY (node_job))"
|
||||||
if _, err := db.Exec(create_query); err != nil {
|
if _, err := db.Exec(create_query); err != nil {
|
||||||
return nil, errwrap.Wrapf("failed to create mysql table: {{err}}", err)
|
return nil, errwrap.Wrapf("failed to create mysql table: {{err}}", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the backend.
|
// Setup the backend.
|
||||||
m := &MySQLBackend{
|
m := &MySQLBackend{
|
||||||
dbTable: dbTable,
|
dbTable: dbTable,
|
||||||
client: db,
|
dbLockTable: dbLockTable,
|
||||||
statements: make(map[string]*sql.Stmt),
|
client: db,
|
||||||
logger: logger,
|
statements: make(map[string]*sql.Stmt),
|
||||||
permitPool: physical.NewPermitPool(maxParInt),
|
logger: logger,
|
||||||
conf: conf,
|
permitPool: physical.NewPermitPool(maxParInt),
|
||||||
|
conf: conf,
|
||||||
|
haEnabled: haEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare all the statements required
|
// Prepare all the statements required
|
||||||
statements := map[string]string{
|
statements := map[string]string{
|
||||||
"put": "INSERT INTO " + dbTable +
|
"put": "INSERT INTO " + dbTable +
|
||||||
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE vault_value=VALUES(vault_value)",
|
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE vault_value=VALUES(vault_value)",
|
||||||
"get": "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
|
"get": "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
|
||||||
"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
|
"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
|
||||||
"list": "SELECT vault_key FROM " + dbTable + " WHERE vault_key LIKE ?",
|
"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(?)",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
for name, query := range statements {
|
||||||
if err := m.prepare(name, query); err != nil {
|
if err := m.prepare(name, query); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -364,7 +386,7 @@ func (m *MySQLBackend) LockWith(key, value string) (physical.Lock, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MySQLBackend) HAEnabled() bool {
|
func (m *MySQLBackend) HAEnabled() bool {
|
||||||
return true
|
return m.haEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// MySQLHALock is a MySQL Lock implementation for the HABackend
|
// 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.
|
// the rest of the MySQL backend and any cleanup that might need to be done.
|
||||||
conn, _ := NewMySQLClient(in.conf, in.logger)
|
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{
|
m := &MySQLLock{
|
||||||
parentConn: in,
|
parentConn: in,
|
||||||
in: conn,
|
in: conn,
|
||||||
@ -571,7 +581,7 @@ func NewMySQLLock(in *MySQLBackend, l log.Logger, key, value string) (*MySQLLock
|
|||||||
}
|
}
|
||||||
|
|
||||||
statements := map[string]string{
|
statements := map[string]string{
|
||||||
"put": "INSERT INTO " + dbTable +
|
"put": "INSERT INTO " + in.dbLockTable +
|
||||||
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE current_leader=VALUES(current_leader)",
|
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE current_leader=VALUES(current_leader)",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ func TestMySQLBackend(t *testing.T) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
mysql := b.(*MySQLBackend)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Failed to drop table: %v", err)
|
t.Fatalf("Failed to drop table: %v", err)
|
||||||
}
|
}
|
||||||
@ -80,11 +80,12 @@ func TestMySQLHABackend(t *testing.T) {
|
|||||||
logger := logging.NewVaultLogger(log.Debug)
|
logger := logging.NewVaultLogger(log.Debug)
|
||||||
|
|
||||||
b, err := NewMySQLBackend(map[string]string{
|
b, err := NewMySQLBackend(map[string]string{
|
||||||
"address": address,
|
"address": address,
|
||||||
"database": database,
|
"database": database,
|
||||||
"table": table,
|
"table": table,
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
|
"ha_enabled": "true",
|
||||||
}, logger)
|
}, logger)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -93,7 +94,7 @@ func TestMySQLHABackend(t *testing.T) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
mysql := b.(*MySQLBackend)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Failed to drop table: %v", err)
|
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
|
- `password` `(string: <required)` – Specifies the MySQL password to connect to
|
||||||
the database.
|
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
|
## `mysql` Examples
|
||||||
|
|
||||||
### Custom Database and Table
|
### Custom Database and Table
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user