From 21e6241580372e7099f2d5db8859ed11ef866e07 Mon Sep 17 00:00:00 2001 From: Peter Junos Date: Fri, 1 Jul 2022 17:06:45 +0200 Subject: [PATCH] Readd MySQL 5.6 compatiblity, speedup execution --- mysql/provider.go | 11 ++-- mysql/resource_database.go | 9 ++-- mysql/resource_grant.go | 15 +++--- mysql/resource_user.go | 94 ++++++++++++++++++--------------- mysql/resource_user_password.go | 9 +--- 5 files changed, 70 insertions(+), 68 deletions(-) diff --git a/mysql/provider.go b/mysql/provider.go index 6b596d82..46d7c284 100644 --- a/mysql/provider.go +++ b/mysql/provider.go @@ -33,6 +33,7 @@ type MySQLConfiguration struct { MaxConnLifetime time.Duration MaxOpenConns int ConnectRetryTimeoutSec time.Duration + Version *version.Version } var ( @@ -198,24 +199,26 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { } mysqlConf.Db = db - if err := afterConnect(d, db); err != nil { + if err := afterConnect(mysqlConf, db); err != nil { return nil, fmt.Errorf("Failed running after connect command: %v", err) } return mysqlConf, nil } -func afterConnect(d *schema.ResourceData, db *sql.DB) error { +func afterConnect(mysqlConf *MySQLConfiguration, db *sql.DB) error { // Set up env so that we won't create users randomly. currentVersion, err := serverVersion(db) if err != nil { return fmt.Errorf("Failed getting server version: %v", err) } + mysqlConf.Version = currentVersion + versionMinInclusive, _ := version.NewVersion("5.7.5") versionMaxExclusive, _ := version.NewVersion("8.0.0") - if currentVersion.GreaterThanOrEqual(versionMinInclusive) && - currentVersion.LessThan(versionMaxExclusive) { + if mysqlConf.Version.GreaterThanOrEqual(versionMinInclusive) && + mysqlConf.Version.LessThan(versionMaxExclusive) { // CONCAT and setting works even if there is no value. _, err := db.Exec(`SET SESSION sql_mode=CONCAT(@@sql_mode, ',NO_AUTO_CREATE_USER')`) if err != nil { diff --git a/mysql/resource_database.go b/mysql/resource_database.go index 9ca5b9b9..fe352b55 100644 --- a/mysql/resource_database.go +++ b/mysql/resource_database.go @@ -77,7 +77,8 @@ func UpdateDatabase(d *schema.ResourceData, meta interface{}) error { } func ReadDatabase(d *schema.ResourceData, meta interface{}) error { - db := meta.(*MySQLConfiguration).Db + mysqlConf := meta.(*MySQLConfiguration) + db := mysqlConf.Db // This is kinda flimsy-feeling, since it depends on the formatting // of the SHOW CREATE DATABASE output... but this data doesn't seem @@ -111,10 +112,6 @@ func ReadDatabase(d *schema.ResourceData, meta interface{}) error { var empty interface{} requiredVersion, _ := version.NewVersion("8.0.0") - currentVersion, err := serverVersion(db) - if err != nil { - return err - } serverVersionString, err := serverVersionString(db) if err != nil { @@ -123,7 +120,7 @@ func ReadDatabase(d *schema.ResourceData, meta interface{}) error { // MySQL 8 returns more data in a row. var res error - if !strings.Contains(serverVersionString, "MariaDB") && currentVersion.GreaterThan(requiredVersion) { + if !strings.Contains(serverVersionString, "MariaDB") && mysqlConf.Version.GreaterThan(requiredVersion) { res = db.QueryRow(stmtSQL, defaultCharset).Scan(&defaultCollation, &empty, &empty, &empty, &empty, &empty, &empty) } else { res = db.QueryRow(stmtSQL, defaultCharset).Scan(&defaultCollation, &empty, &empty, &empty, &empty, &empty) diff --git a/mysql/resource_grant.go b/mysql/resource_grant.go index aeb87495..70cb0245 100644 --- a/mysql/resource_grant.go +++ b/mysql/resource_grant.go @@ -146,11 +146,8 @@ func userOrRole(user string, host string, role string, hasRoles bool) (string, b } } -func supportsRoles(db *sql.DB) (bool, error) { - currentVersion, err := serverVersion(db) - if err != nil { - return false, err - } +func supportsRoles(meta interface{}) (bool, error) { + currentVersion := meta.(*MySQLConfiguration).Version requiredVersion, _ := version.NewVersion("8.0.0") hasRoles := currentVersion.GreaterThan(requiredVersion) @@ -160,7 +157,7 @@ func supportsRoles(db *sql.DB) (bool, error) { func CreateGrant(d *schema.ResourceData, meta interface{}) error { db := meta.(*MySQLConfiguration).Db - hasRoles, err := supportsRoles(db) + hasRoles, err := supportsRoles(meta) if err != nil { return err } @@ -263,7 +260,7 @@ func CreateGrant(d *schema.ResourceData, meta interface{}) error { func ReadGrant(d *schema.ResourceData, meta interface{}) error { db := meta.(*MySQLConfiguration).Db - hasRoles, err := supportsRoles(db) + hasRoles, err := supportsRoles(meta) if err != nil { return err } @@ -323,7 +320,7 @@ func ReadGrant(d *schema.ResourceData, meta interface{}) error { func UpdateGrant(d *schema.ResourceData, meta interface{}) error { db := meta.(*MySQLConfiguration).Db - hasRoles, err := supportsRoles(db) + hasRoles, err := supportsRoles(meta) if err != nil { return err @@ -402,7 +399,7 @@ func DeleteGrant(d *schema.ResourceData, meta interface{}) error { table := formatTableName(d.Get("table").(string)) - hasRoles, err := supportsRoles(db) + hasRoles, err := supportsRoles(meta) if err != nil { return err } diff --git a/mysql/resource_user.go b/mysql/resource_user.go index 3b665438..67e25b01 100644 --- a/mysql/resource_user.go +++ b/mysql/resource_user.go @@ -1,7 +1,6 @@ package mysql import ( - "database/sql" "fmt" "log" "regexp" @@ -69,7 +68,6 @@ func resourceUser() *schema.Resource { "tls_option": { Type: schema.TypeString, Optional: true, - ForceNew: true, Default: "NONE", }, }, @@ -122,17 +120,13 @@ func CreateUser(d *schema.ResourceData, meta interface{}) error { } requiredVersion, _ := version.NewVersion("5.7.0") - currentVersion, err := serverVersion(db) - if err != nil { - return err - } - if currentVersion.GreaterThan(requiredVersion) && d.Get("tls_option").(string) != "" { + if meta.(*MySQLConfiguration).Version.GreaterThan(requiredVersion) && d.Get("tls_option").(string) != "" { stmtSQL += fmt.Sprintf(" REQUIRE %s", d.Get("tls_option").(string)) } log.Println("Executing statement:", stmtSQL) - _, err = db.Exec(stmtSQL) + _, err := db.Exec(stmtSQL) if err != nil { return err } @@ -143,15 +137,10 @@ func CreateUser(d *schema.ResourceData, meta interface{}) error { return nil } -func getSetPasswordStatement(db *sql.DB) (string, error) { +func getSetPasswordStatement(meta interface{}) (string, error) { /* ALTER USER syntax introduced in MySQL 5.7.6 deprecates SET PASSWORD (GH-8230) */ - serverVersion, err := serverVersion(db) - if err != nil { - return "", fmt.Errorf("Could not determine server version: %s", err) - } - ver, _ := version.NewVersion("5.7.6") - if serverVersion.LessThan(ver) { + if meta.(*MySQLConfiguration).Version.LessThan(ver) { return "SET PASSWORD FOR ?@? = PASSWORD(?)", nil } else { return "ALTER USER ?@? IDENTIFIED BY ?", nil @@ -159,13 +148,13 @@ func getSetPasswordStatement(db *sql.DB) (string, error) { } func UpdateUser(d *schema.ResourceData, meta interface{}) error { - db := meta.(*MySQLConfiguration).Db + mysqlConf := meta.(*MySQLConfiguration) + db := mysqlConf.Db var auth string if v, ok := d.GetOk("auth_plugin"); ok { auth = v.(string) } - if len(auth) > 0 { if d.HasChange("tls_option") || d.HasChange("auth_plugin") || d.HasChange("auth_string_hashed") { var stmtSQL string @@ -201,7 +190,7 @@ func UpdateUser(d *schema.ResourceData, meta interface{}) error { } if newpw != nil { - stmtSQL, err := getSetPasswordStatement(db) + stmtSQL, err := getSetPasswordStatement(meta) if err != nil { return err } @@ -217,12 +206,7 @@ func UpdateUser(d *schema.ResourceData, meta interface{}) error { } requiredVersion, _ := version.NewVersion("5.7.0") - currentVersion, err := serverVersion(db) - if err != nil { - return err - } - - if d.HasChange("tls_option") && currentVersion.GreaterThan(requiredVersion) { + if d.HasChange("tls_option") && mysqlConf.Version.GreaterThan(requiredVersion) { var stmtSQL string stmtSQL = fmt.Sprintf("ALTER USER '%s'@'%s' REQUIRE %s", @@ -242,27 +226,53 @@ func UpdateUser(d *schema.ResourceData, meta interface{}) error { func ReadUser(d *schema.ResourceData, meta interface{}) error { db := meta.(*MySQLConfiguration).Db - stmt := "SHOW CREATE USER ?@?" + currentVersion := meta.(*MySQLConfiguration).Version + requiredVersion, _ := version.NewVersion("5.7.0") + if currentVersion.GreaterThan(requiredVersion) { + stmt := "SHOW CREATE USER ?@?" - var createUserStmt string - err := db.QueryRow(stmt, d.Get("user").(string), d.Get("host").(string)).Scan(&createUserStmt) - if err != nil { - if mysqlErrorNumber(err) == unknownUserErrCode { - d.SetId("") - return nil + var createUserStmt string + err := db.QueryRow(stmt, d.Get("user").(string), d.Get("host").(string)).Scan(&createUserStmt) + if err != nil { + if mysqlErrorNumber(err) == unknownUserErrCode { + d.SetId("") + return nil + } + return err } - return err - } - // CREATE USER 'some_app'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*0something' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK - re := regexp.MustCompile(`^CREATE USER '([^']*)'@'([^']*)' IDENTIFIED WITH '([^']*)' (?:AS '([^']*)' )?REQUIRE ([^ ]*)`) - if m := re.FindStringSubmatch(createUserStmt); len(m) == 6 { - d.Set("user", m[1]) - d.Set("host", m[2]) - d.Set("auth_plugin", m[3]) - d.Set("auth_string_hashed", m[4]) - d.Set("tls_option", m[5]) + + // Examples of create user: + // CREATE USER 'some_app'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*0something' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK + // CREATE USER `jdoe-tf-test-47`@`example.com` IDENTIFIED WITH 'caching_sha2_password' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT PASSWORD REQUIRE CURRENT DEFAULT + // CREATE USER `jdoe`@`example.com` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$i`xay#fG/\' TrbkNA82' REQUIRE NONE PASSWORD + re := regexp.MustCompile("^CREATE USER ['`]([^'`]*)['`]@['`]([^'`]*)['`] IDENTIFIED WITH ['`]([^'`]*)['`] (?:AS '((?:.*?[^\\\\])?)' )?REQUIRE ([^ ]*)") + if m := re.FindStringSubmatch(createUserStmt); len(m) == 6 { + d.Set("user", m[1]) + d.Set("host", m[2]) + d.Set("auth_plugin", m[3]) + d.Set("auth_string_hashed", m[4]) + d.Set("tls_option", m[5]) + } else { + return fmt.Errorf("Create user couldn't be parsed - it is %s", createUserStmt) + } + return nil } else { - return fmt.Errorf("Create user couldn't be parsed - it is %s", createUserStmt) + // Worse user detection, only for compat with MySQL 5.6 + stmtSQL := fmt.Sprintf("SELECT USER FROM mysql.user WHERE USER='%s'", + d.Get("user").(string)) + + log.Println("Executing statement:", stmtSQL) + + rows, err := db.Query(stmtSQL) + if err != nil { + return err + } + defer rows.Close() + + if !rows.Next() && rows.Err() == nil { + d.SetId("") + return rows.Err() + } } return nil } diff --git a/mysql/resource_user_password.go b/mysql/resource_user_password.go index 7ab00358..099335e5 100644 --- a/mysql/resource_user_password.go +++ b/mysql/resource_user_password.go @@ -49,7 +49,7 @@ func SetUserPassword(d *schema.ResourceData, meta interface{}) error { d.Set("plaintext_password", password) } - stmtSQL, err := getSetPasswordStatement(db) + stmtSQL, err := getSetPasswordStatement(meta) if err != nil { return err } @@ -68,12 +68,7 @@ func SetUserPassword(d *schema.ResourceData, meta interface{}) error { } func canReadPassword(meta interface{}) (bool, error) { - db := meta.(*MySQLConfiguration).Db - serverVersion, err := serverVersion(db) - if err != nil { - return false, fmt.Errorf("Could not determine server version: %s", err) - } - + serverVersion := meta.(*MySQLConfiguration).Version ver, _ := version.NewVersion("8.0.0") return serverVersion.LessThan(ver), nil }