diff --git a/config/mycnf/master_mariadb.cnf b/config/mycnf/master_mariadb.cnf index 83f6f318e4c..1e41cd8f3ce 100644 --- a/config/mycnf/master_mariadb.cnf +++ b/config/mycnf/master_mariadb.cnf @@ -1,16 +1,8 @@ +# This file is auto-included when MariaDB (any version) is detected. + # enable strict mode so it's safe to compare sequence numbers across different server IDs. gtid_strict_mode = 1 innodb_stats_persistent = 0 -innodb_support_xa = 0 - -# Semi-sync replication is required for automated unplanned failover -# (when the master goes away). Here we just load the plugin so it's -# available if desired, but it's disabled at startup. -# -# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync -# at the proper time when replication is set up, or when masters are -# promoted or demoted. -plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so # When semi-sync is enabled, don't allow fallback to async # if you get no ack, or have no slaves. This is necessary to diff --git a/config/mycnf/master_mariadb100.cnf b/config/mycnf/master_mariadb100.cnf new file mode 100644 index 00000000000..ce85c641c13 --- /dev/null +++ b/config/mycnf/master_mariadb100.cnf @@ -0,0 +1,12 @@ +# This file is auto-included when MariaDB 10.0 is detected. + +innodb_support_xa = 0 + +# Semi-sync replication is required for automated unplanned failover +# (when the master goes away). Here we just load the plugin so it's +# available if desired, but it's disabled at startup. +# +# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync +# at the proper time when replication is set up, or when masters are +# promoted or demoted. +plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so diff --git a/config/mycnf/master_mariadb101.cnf b/config/mycnf/master_mariadb101.cnf new file mode 100644 index 00000000000..d613b155d68 --- /dev/null +++ b/config/mycnf/master_mariadb101.cnf @@ -0,0 +1,12 @@ +# This file is auto-included when MariaDB 10.1 is detected. + +innodb_support_xa = 0 + +# Semi-sync replication is required for automated unplanned failover +# (when the master goes away). Here we just load the plugin so it's +# available if desired, but it's disabled at startup. +# +# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync +# at the proper time when replication is set up, or when masters are +# promoted or demoted. +plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so diff --git a/config/mycnf/master_mariadb102.cnf b/config/mycnf/master_mariadb102.cnf new file mode 100644 index 00000000000..487baa9bf87 --- /dev/null +++ b/config/mycnf/master_mariadb102.cnf @@ -0,0 +1,12 @@ +# This file is auto-included when MariaDB 10.2 is detected. + +innodb_support_xa = 0 + +# Semi-sync replication is required for automated unplanned failover +# (when the master goes away). Here we just load the plugin so it's +# available if desired, but it's disabled at startup. +# +# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync +# at the proper time when replication is set up, or when masters are +# promoted or demoted. +plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so diff --git a/config/mycnf/master_mariadb103.cnf b/config/mycnf/master_mariadb103.cnf index a4dfb50ab18..ac8b38404fd 100644 --- a/config/mycnf/master_mariadb103.cnf +++ b/config/mycnf/master_mariadb103.cnf @@ -1,3 +1,5 @@ +# This file is auto-included when MariaDB 10.3 is detected. + # enable strict mode so it's safe to compare sequence numbers across different server IDs. gtid_strict_mode = 1 innodb_stats_persistent = 0 diff --git a/config/mycnf/master_mysql56.cnf b/config/mycnf/master_mysql56.cnf index 2c802b2332a..dcb8a4e113f 100644 --- a/config/mycnf/master_mysql56.cnf +++ b/config/mycnf/master_mysql56.cnf @@ -1,3 +1,5 @@ +# This file is auto-included when MySQL 5.6 is detected. + # Options for enabling GTID # https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-howto.html gtid_mode = ON diff --git a/config/mycnf/master_mysql57.cnf b/config/mycnf/master_mysql57.cnf new file mode 100644 index 00000000000..381b05ac14c --- /dev/null +++ b/config/mycnf/master_mysql57.cnf @@ -0,0 +1,32 @@ +# This file is auto-included when MySQL 5.7 is detected. + +# Options for enabling GTID +# https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-howto.html +gtid_mode = ON +log_bin +log_slave_updates +enforce_gtid_consistency +innodb_use_native_aio = 0 + +# Crash-safe replication settings. +master_info_repository = TABLE +relay_log_info_repository = TABLE +relay_log_purge = 1 +relay_log_recovery = 1 + +# Semi-sync replication is required for automated unplanned failover +# (when the master goes away). Here we just load the plugin so it's +# available if desired, but it's disabled at startup. +# +# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync +# at the proper time when replication is set up, or when masters are +# promoted or demoted. +plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so + +# When semi-sync is enabled, don't allow fallback to async +# if you get no ack, or have no slaves. This is necessary to +# prevent alternate futures when doing a failover in response to +# a master that becomes unresponsive. +rpl_semi_sync_master_timeout = 1000000000000000000 +rpl_semi_sync_master_wait_no_slave = 1 + diff --git a/config/mycnf/master_mysql80.cnf b/config/mycnf/master_mysql80.cnf index f81761ad906..e92b794ef9b 100644 --- a/config/mycnf/master_mysql80.cnf +++ b/config/mycnf/master_mysql80.cnf @@ -1,3 +1,5 @@ +# This file is auto-included when MySQL 8.0 is detected. + # Options for enabling GTID # https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-howto.html gtid_mode = ON diff --git a/dev.env b/dev.env index e31c16167e2..ca7bc608721 100644 --- a/dev.env +++ b/dev.env @@ -95,16 +95,6 @@ if [[ "$VT_MYSQL_ROOT" == "" ]]; then fi fi -# restore MYSQL_FLAVOR, saved by bootstrap.sh -if [ -r "$VTROOT/dist/MYSQL_FLAVOR" ]; then - MYSQL_FLAVOR=$(cat "$VTROOT/dist/MYSQL_FLAVOR") - export MYSQL_FLAVOR -fi - -# mysql cgo library config -if [ -z "$MYSQL_FLAVOR" ]; then - export MYSQL_FLAVOR=MariaDB -fi PKG_CONFIG_PATH=$(prepend_path "$PKG_CONFIG_PATH" "$VTROOT/lib") export PKG_CONFIG_PATH diff --git a/examples/local/env.sh b/examples/local/env.sh index 608725a2487..f285de810ee 100644 --- a/examples/local/env.sh +++ b/examples/local/env.sh @@ -29,15 +29,8 @@ if [ -z "$VT_MYSQL_ROOT" ]; then export VT_MYSQL_ROOT=$(dirname `dirname $mysql_path`) fi -# restore MYSQL_FLAVOR, saved by bootstrap.sh -if [ -r "$VTROOT/dist/MYSQL_FLAVOR" ]; then - MYSQL_FLAVOR=$(cat "$VTROOT/dist/MYSQL_FLAVOR") - export MYSQL_FLAVOR -fi - -if [ -z "$MYSQL_FLAVOR" ]; then - export MYSQL_FLAVOR=MySQL56 -fi +# Previously the file specified MYSQL_FLAVOR +# it is now autodetected if [ "${TOPO}" = "etcd2" ]; then echo "enter etcd2 env" diff --git a/examples/local/vttablet-up.sh b/examples/local/vttablet-up.sh index 0acd5e51b9f..75c3b191d04 100755 --- a/examples/local/vttablet-up.sh +++ b/examples/local/vttablet-up.sh @@ -38,23 +38,10 @@ source $script_root/env.sh init_db_sql_file="$VTROOT/config/init_db.sql" -export EXTRA_MY_CNF=$VTROOT/config/mycnf/default-fast.cnf:$VTROOT/config/mycnf/rbr.cnf +# Previously this file set EXTRA_MY_CNF based on MYSQL_FLAVOR +# It now relies on mysqlctl to autodetect -case "$MYSQL_FLAVOR" in - "MySQL56") - export EXTRA_MY_CNF=$EXTRA_MY_CNF:$VTROOT/config/mycnf/master_mysql56.cnf - ;; - "MariaDB") - export EXTRA_MY_CNF=$EXTRA_MY_CNF:$VTROOT/config/mycnf/master_mariadb.cnf - ;; - "MariaDB103") - export EXTRA_MY_CNF=$EXTRA_MY_CNF:$VTROOT/config/mycnf/master_mariadb103.cnf - ;; - *) - echo "Please set MYSQL_FLAVOR to MySQL56 or MariaDB." - exit 1 - ;; -esac +export EXTRA_MY_CNF=$VTROOT/config/mycnf/default-fast.cnf:$VTROOT/config/mycnf/rbr.cnf mkdir -p $VTDATAROOT/backups diff --git a/go/vt/mysqlctl/capabilityset.go b/go/vt/mysqlctl/capabilityset.go new file mode 100644 index 00000000000..568c75bd096 --- /dev/null +++ b/go/vt/mysqlctl/capabilityset.go @@ -0,0 +1,79 @@ +/* +Copyright 2019 The Vitess Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + Detect server flavors and capabilities +*/ + +package mysqlctl + +type mysqlFlavor string + +const ( + flavorMySQL mysqlFlavor = "mysql" + flavorPercona mysqlFlavor = "percona" + flavorMariaDB mysqlFlavor = "mariadb" +) + +// Mysqld is the object that represents a mysqld daemon running on this server. +type CapabilitySet struct { + flavor mysqlFlavor + version serverVersion +} + +func NewCapabilitySet(f mysqlFlavor, v serverVersion) (c CapabilitySet) { + c.flavor = f + c.version = v + return +} + +func (c *CapabilitySet) HasMySQLUpgradeInServer() bool { + return c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 8, Minor: 0, Patch: 16}) +} +func (c *CapabilitySet) HasInitializeInServer() bool { + return c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 5, Minor: 7, Patch: 0}) +} +func (c *CapabilitySet) HasMySQLxEnabledByDefault() bool { + return c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 8, Minor: 0, Patch: 11}) +} +func (c *CapabilitySet) HasPersistConfig() bool { + return c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 8, Minor: 0, Patch: 0}) +} +func (c *CapabilitySet) HasShutdownCommand() bool { + return (c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 5, Minor: 7, Patch: 9})) || (c.IsMariaDB() && c.version.atLeast(serverVersion{Major: 10, Minor: 0, Patch: 4})) +} +func (c *CapabilitySet) HasBackupLocks() bool { + return c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 8, Minor: 0, Patch: 0}) +} +func (c *CapabilitySet) HasDefaultUft8mb4() bool { + return c.IsMySQLLike() && c.version.atLeast(serverVersion{Major: 8, Minor: 0, Patch: 0}) +} +func (c *CapabilitySet) HasSemiSyncEnabledByDefault() bool { + return c.IsMariaDB() && c.version.atLeast(serverVersion{Major: 10, Minor: 3, Patch: 3}) +} + +// IsMySQLLike tests if the server is either MySQL +// or Percona Server. At least currently, Vitess doesn't +// make use of any specific Percona Server features. +func (c *CapabilitySet) IsMySQLLike() bool { + return c.flavor == flavorMySQL || c.flavor == flavorPercona +} + +// IsMariaDB tests if the server is MariaDB. +// IsMySQLLike() and IsMariaDB() are mutually exclusive +func (c *CapabilitySet) IsMariaDB() bool { + return c.flavor == flavorMariaDB +} diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index 7279265894c..db29d6a0dd6 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -25,6 +25,7 @@ package mysqlctl import ( "bufio" + "bytes" "errors" "flag" "fmt" @@ -34,12 +35,12 @@ import ( "os/exec" "path" "path/filepath" + "regexp" + "strconv" "strings" "sync" "time" - "bytes" - "golang.org/x/net/context" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/stats" @@ -78,6 +79,8 @@ var ( dbaMysqlStats = stats.NewTimings("MysqlDba", "MySQL DBA stats", "operation") allprivsMysqlStats = stats.NewTimings("MysqlAllPrivs", "MySQl Stats for all privs", "operation") appMysqlStats = stats.NewTimings("MysqlApp", "MySQL app stats", "operation") + + versionRegex = regexp.MustCompile(`Ver ([0-9]+)\.([0-9]+)\.([0-9]+)`) ) // Mysqld is the object that represents a mysqld daemon running on this server. @@ -86,6 +89,8 @@ type Mysqld struct { dbaPool *dbconnpool.ConnectionPool appPool *dbconnpool.ConnectionPool + capabilities CapabilitySet + // mutex protects the fields below. mutex sync.Mutex onTermFuncs []func() @@ -107,9 +112,95 @@ func NewMysqld(dbcfgs *dbconfigs.DBConfigs) *Mysqld { result.appPool = dbconnpool.NewConnectionPool("AppConnPool", *appPoolSize, *appIdleTimeout, *poolDynamicHostnameResolution) result.appPool.Open(dbcfgs.AppWithDB(), appMysqlStats) + version, getErr := getVersionString() + f, v, err := parseVersionString(version) + + // Fallback if required + if getErr != nil || err != nil { + f, v, err = getVersionFromEnv() + if err != nil { + panic("Could not detect version from mysqld --version or MYSQL_FLAVOR") + } + + } + + log.Infof("Using flavor: %v, version: %v", f, v) + result.capabilities = NewCapabilitySet(f, v) return result } +/* +getVersionFromEnv returns the flavor and an assumed version based on the legacy +MYSQL_FLAVOR environment variable. + +The assumed version may not be accurate since the legacy variable only specifies +broad families of compatible versions. However, the differences between those +versions should only matter if Vitess is managing the lifecycle of mysqld, in which +case we should have a local copy of the mysqld binary from which we can fetch +the accurate version instead of falling back to this function (see getVersionString). +*/ +func getVersionFromEnv() (flavor mysqlFlavor, ver serverVersion, err error) { + env := os.Getenv("MYSQL_FLAVOR") + switch env { + case "MariaDB": + return flavorMariaDB, serverVersion{10, 0, 10}, nil + case "MariaDB103": + return flavorMariaDB, serverVersion{10, 3, 7}, nil + case "MySQL80": + return flavorMySQL, serverVersion{8, 0, 11}, nil + case "MySQL56": + return flavorMySQL, serverVersion{5, 7, 10}, nil + } + return flavor, ver, fmt.Errorf("Could not determine version from MYSQL_FLAVOR: %s", env) +} + +func getVersionString() (string, error) { + mysqlRoot, err := vtenv.VtMysqlRoot() + if err != nil { + return "", err + } + mysqldPath, err := binaryPath(mysqlRoot, "mysqld") + if err != nil { + return "", err + } + _, version, err := execCmd(mysqldPath, []string{"--version"}, nil, mysqlRoot, nil) + if err != nil { + return "", err + } + return version, nil +} + +// parse the output of mysqld --version into a flavor and version +func parseVersionString(version string) (flavor mysqlFlavor, ver serverVersion, err error) { + if strings.Contains(version, "Percona") { + flavor = flavorPercona + } else if strings.Contains(version, "MariaDB") { + flavor = flavorMariaDB + } else { + // OS distributed MySQL releases have a version string like: + // mysqld Ver 5.7.27-0ubuntu0.19.04.1 for Linux on x86_64 ((Ubuntu)) + flavor = flavorMySQL + } + v := versionRegex.FindStringSubmatch(version) + if len(v) != 4 { + return flavor, ver, fmt.Errorf("Could not parse server version from: %s", version) + } + ver.Major, err = strconv.Atoi(string(v[1])) + if err != nil { + return flavor, ver, fmt.Errorf("Could not parse server version from: %s", version) + } + ver.Minor, err = strconv.Atoi(string(v[2])) + if err != nil { + return flavor, ver, fmt.Errorf("Could not parse server version from: %s", version) + } + ver.Patch, err = strconv.Atoi(string(v[3])) + if err != nil { + return flavor, ver, fmt.Errorf("Could not parse server version from: %s", version) + } + + return +} + // RunMysqlUpgrade will run the mysql_upgrade program on the current // install. Will be called only when mysqld is running with no // network and no grant tables. @@ -125,6 +216,11 @@ func (mysqld *Mysqld) RunMysqlUpgrade() error { return client.RunMysqlUpgrade(context.TODO()) } + if mysqld.capabilities.HasMySQLUpgradeInServer() { + log.Warningf("MySQL version has built-in upgrade, skipping RunMySQLUpgrade") + return nil + } + // Find mysql_upgrade. If not there, we do nothing. dir, err := vtenv.VtMysqlRoot() if err != nil { @@ -540,13 +636,6 @@ func (mysqld *Mysqld) Init(ctx context.Context, cnf *Mycnf, initDBSQLFile string return nil } -// MySQL 5.7 GA and up have deprecated mysql_install_db. -// Instead, initialization is built into mysqld. -func useMysqldInitialize(version string) bool { - return strings.Contains(version, "Ver 5.7.") || - strings.Contains(version, "Ver 8.0.") -} - func (mysqld *Mysqld) installDataDir(cnf *Mycnf) error { mysqlRoot, err := vtenv.VtMysqlRoot() if err != nil { @@ -561,16 +650,8 @@ func (mysqld *Mysqld) installDataDir(cnf *Mycnf) error { if err != nil { return err } - - // Check mysqld version. - _, version, err := execCmd(mysqldPath, []string{"--version"}, nil, mysqlRoot, nil) - if err != nil { - return err - } - - if useMysqldInitialize(version) { + if mysqld.capabilities.HasInitializeInServer() { log.Infof("Installing data dir with mysqld --initialize-insecure") - args := []string{ "--defaults-file=" + cnf.path, "--basedir=" + mysqlBaseDir, @@ -612,7 +693,7 @@ func (mysqld *Mysqld) initConfig(root string, cnf *Mycnf, outFile string) error switch hr := hook.NewHookWithEnv("make_mycnf", nil, env).Execute(); hr.ExitStatus { case hook.HOOK_DOES_NOT_EXIST: log.Infof("make_mycnf hook doesn't exist, reading template files") - configData, err = cnf.makeMycnf(getMycnfTemplates(root)) + configData, err = cnf.makeMycnf(mysqld.getMycnfTemplates(root)) case hook.HOOK_SUCCESS: configData, err = cnf.fillMycnfTemplate(hr.Stdout) default: @@ -634,7 +715,7 @@ func contains(haystack []string, needle string) bool { return false } -func getMycnfTemplates(root string) []string { +func (mysqld *Mysqld) getMycnfTemplates(root string) []string { if *mycnfTemplateFile != "" { return []string{*mycnfTemplateFile} } @@ -650,28 +731,26 @@ func getMycnfTemplates(root string) []string { cnfTemplatePaths = append(cnfTemplatePaths, parts...) } - switch mysqlFlavor := os.Getenv("MYSQL_FLAVOR"); mysqlFlavor { - case "MariaDB": - path := path.Join(root, "config/mycnf/master_mariadb.cnf") - if !contains(cnfTemplatePaths, path) { - cnfTemplatePaths = append(cnfTemplatePaths, path) - } - case "MariaDB103": - path := path.Join(root, "config/mycnf/master_mariadb103.cnf") - if !contains(cnfTemplatePaths, path) { - cnfTemplatePaths = append(cnfTemplatePaths, path) - } - case "MySQL80": - path := path.Join(root, "config/mycnf/master_mysql80.cnf") - if !contains(cnfTemplatePaths, path) { - cnfTemplatePaths = append(cnfTemplatePaths, path) - } - default: - path := path.Join(root, "config/mycnf/master_mysql56.cnf") - // By default we assume Mysql56 compatable - if !contains(cnfTemplatePaths, path) { - cnfTemplatePaths = append(cnfTemplatePaths, path) - } + // Only include these files if they exist. + // master_{flavor}.cnf + // Percona Server == MySQL in this context + + f := flavorMariaDB + if mysqld.capabilities.IsMySQLLike() { + f = flavorMySQL + } + + p := path.Join(root, fmt.Sprintf("config/mycnf/master_%s.cnf", f)) + _, err := os.Stat(p) + if err == nil && !contains(cnfTemplatePaths, p) { + cnfTemplatePaths = append(cnfTemplatePaths, p) + } + + // master_{flavor}{major}{minor}.cnf + p = path.Join(root, fmt.Sprintf("config/mycnf/master_%s%d%d.cnf", f, mysqld.capabilities.version.Major, mysqld.capabilities.version.Minor)) + _, err = os.Stat(p) + if err == nil && !contains(cnfTemplatePaths, p) { + cnfTemplatePaths = append(cnfTemplatePaths, p) } return cnfTemplatePaths diff --git a/go/vt/mysqlctl/mysqld_test.go b/go/vt/mysqlctl/mysqld_test.go new file mode 100644 index 00000000000..f3e45b87e18 --- /dev/null +++ b/go/vt/mysqlctl/mysqld_test.go @@ -0,0 +1,146 @@ +/* +Copyright 2019 The Vitess Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctl + +import ( + "os" + "testing" +) + +type testcase struct { + versionString string + version serverVersion + flavor mysqlFlavor +} + +func TestParseVersionString(t *testing.T) { + + var testcases = []testcase{ + + { + versionString: "mysqld Ver 5.7.27-0ubuntu0.19.04.1 for Linux on x86_64 ((Ubuntu))", + version: serverVersion{5, 7, 27}, + flavor: flavorMySQL, + }, + { + versionString: "mysqld Ver 5.6.43 for linux-glibc2.12 on x86_64 (MySQL Community Server (GPL))", + version: serverVersion{5, 6, 43}, + flavor: flavorMySQL, + }, + { + versionString: "mysqld Ver 5.7.26 for linux-glibc2.12 on x86_64 (MySQL Community Server (GPL))", + version: serverVersion{5, 7, 26}, + flavor: flavorMySQL, + }, + { + versionString: "mysqld Ver 8.0.16 for linux-glibc2.12 on x86_64 (MySQL Community Server - GPL)", + version: serverVersion{8, 0, 16}, + flavor: flavorMySQL, + }, + { + versionString: "mysqld Ver 5.7.26-29 for Linux on x86_64 (Percona Server (GPL), Release 29, Revision 11ad961)", + version: serverVersion{5, 7, 26}, + flavor: flavorPercona, + }, + { + versionString: "mysqld Ver 10.0.38-MariaDB for Linux on x86_64 (MariaDB Server)", + version: serverVersion{10, 0, 38}, + flavor: flavorMariaDB, + }, + { + versionString: "mysqld Ver 10.1.40-MariaDB for Linux on x86_64 (MariaDB Server)", + version: serverVersion{10, 1, 40}, + flavor: flavorMariaDB, + }, + { + versionString: "mysqld Ver 10.2.25-MariaDB for Linux on x86_64 (MariaDB Server)", + version: serverVersion{10, 2, 25}, + flavor: flavorMariaDB, + }, + { + versionString: "mysqld Ver 10.3.16-MariaDB for Linux on x86_64 (MariaDB Server)", + version: serverVersion{10, 3, 16}, + flavor: flavorMariaDB, + }, + { + versionString: "mysqld Ver 10.4.6-MariaDB for Linux on x86_64 (MariaDB Server)", + version: serverVersion{10, 4, 6}, + flavor: flavorMariaDB, + }, + { + versionString: "mysqld Ver 5.6.42 for linux-glibc2.12 on x86_64 (MySQL Community Server (GPL))", + version: serverVersion{5, 6, 42}, + flavor: flavorMySQL, + }, + { + versionString: "mysqld Ver 5.6.44-86.0 for Linux on x86_64 (Percona Server (GPL), Release 86.0, Revision eba1b3f)", + version: serverVersion{5, 6, 44}, + flavor: flavorPercona, + }, + { + versionString: "mysqld Ver 8.0.15-6 for Linux on x86_64 (Percona Server (GPL), Release 6, Revision 63abd08)", + version: serverVersion{8, 0, 15}, + flavor: flavorPercona, + }, + } + + for _, testcase := range testcases { + f, v, err := parseVersionString(testcase.versionString) + if v != testcase.version || f != testcase.flavor || err != nil { + t.Errorf("parseVersionString failed for: %#v, Got: %#v, %#v Expected: %#v, %#v", testcase.versionString, v, f, testcase.version, testcase.flavor) + } + } + +} + +func TestAssumeVersionString(t *testing.T) { + + // In these cases, the versionstring is nonsensical or unspecified. + // MYSQL_FLAVOR is used instead. + + var testcases = []testcase{ + { + versionString: "MySQL80", + version: serverVersion{8, 0, 11}, + flavor: flavorMySQL, + }, + { + versionString: "MySQL56", + version: serverVersion{5, 7, 10}, // Yes, this has to lie! + flavor: flavorMySQL, // There was no MySQL57 option + }, + { + versionString: "MariaDB", + version: serverVersion{10, 0, 10}, + flavor: flavorMariaDB, + }, + { + versionString: "MariaDB103", + version: serverVersion{10, 3, 7}, + flavor: flavorMariaDB, + }, + } + + for _, testcase := range testcases { + os.Setenv("MYSQL_FLAVOR", testcase.versionString) + f, v, err := getVersionFromEnv() + if v != testcase.version || f != testcase.flavor || err != nil { + t.Errorf("getVersionFromEnv() failed for: %#v, Got: %#v, %#v Expected: %#v, %#v", testcase.versionString, v, f, testcase.version, testcase.flavor) + } + } + +} diff --git a/go/vt/mysqlctl/version.go b/go/vt/mysqlctl/version.go new file mode 100644 index 00000000000..05bb18c20ef --- /dev/null +++ b/go/vt/mysqlctl/version.go @@ -0,0 +1,38 @@ +/* +Copyright 2019 The Vitess Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + Detect server flavors and capabilities +*/ + +package mysqlctl + +type serverVersion struct { + Major, Minor, Patch int +} + +func (v *serverVersion) atLeast(compare serverVersion) bool { + if v.Major > compare.Major { + return true + } + if v.Major == compare.Major && v.Minor > compare.Minor { + return true + } + if v.Major == compare.Major && v.Minor == compare.Minor && v.Patch >= compare.Patch { + return true + } + return false +}