mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +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,13 +117,25 @@ func NewMySQLBackend(conf map[string]string, logger log.Logger) (physical.Backen | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	locktable, ok := conf["lock_table"] | ||||
| 	// Default value for ha_enabled | ||||
| 	haEnabledStr, ok := conf["ha_enabled"] | ||||
| 	if !ok { | ||||
| 		locktable = "vault_lock" | ||||
| 		haEnabledStr = "false" | ||||
| 	} | ||||
| 	haEnabled, err := strconv.ParseBool(haEnabledStr) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("value [%v] of 'ha_enabled' could not be understood", haEnabledStr) | ||||
| 	} | ||||
|  | ||||
| 	dbLockTable := database + "." + locktable | ||||
| 	locktable, ok := conf["lock_table"] | ||||
| 	if !ok { | ||||
| 		locktable = table + "_lock" | ||||
| 	} | ||||
|  | ||||
| 	dbLockTable := "`" + database + "`.`" + locktable + "`" | ||||
|  | ||||
| 	// 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) | ||||
| @@ -140,15 +154,18 @@ func NewMySQLBackend(conf map[string]string, logger log.Logger) (physical.Backen | ||||
| 				return nil, errwrap.Wrapf("failed to create mysql table: {{err}}", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Setup the backend. | ||||
| 	m := &MySQLBackend{ | ||||
| 		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 | ||||
| @@ -158,9 +175,14 @@ func NewMySQLBackend(conf map[string]string, logger log.Logger) (physical.Backen | ||||
| 		"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(?)", | ||||
| 	} | ||||
|  | ||||
| 	// 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) | ||||
| 		} | ||||
| @@ -85,6 +85,7 @@ func TestMySQLHABackend(t *testing.T) { | ||||
| 		"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
	 brianvans
					brianvans