mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +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. | // 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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 brianvans
					brianvans