diff --git a/go/cmd/mysqlctl/mysqlctl.go b/go/cmd/mysqlctl/mysqlctl.go index 9e87249a9c7..23db8721b78 100644 --- a/go/cmd/mysqlctl/mysqlctl.go +++ b/go/cmd/mysqlctl/mysqlctl.go @@ -48,12 +48,12 @@ func initConfigCmd(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) // Generate my.cnf from scratch and use it to find mysqld. - mysqld, err := mysqlctl.CreateMysqld(uint32(*tabletUID), *mysqlSocket, int32(*mysqlPort)) + mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(uint32(*tabletUID), *mysqlSocket, int32(*mysqlPort)) if err != nil { return fmt.Errorf("failed to initialize mysql config: %v", err) } defer mysqld.Close() - if err := mysqld.InitConfig(); err != nil { + if err := mysqld.InitConfig(cnf); err != nil { return fmt.Errorf("failed to init mysql config: %v", err) } return nil @@ -65,7 +65,7 @@ func initCmd(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) // Generate my.cnf from scratch and use it to find mysqld. - mysqld, err := mysqlctl.CreateMysqld(uint32(*tabletUID), *mysqlSocket, int32(*mysqlPort)) + mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(uint32(*tabletUID), *mysqlSocket, int32(*mysqlPort)) if err != nil { return fmt.Errorf("failed to initialize mysql config: %v", err) } @@ -73,7 +73,7 @@ func initCmd(subFlags *flag.FlagSet, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), *waitTime) defer cancel() - if err := mysqld.Init(ctx, *initDBSQLFile); err != nil { + if err := mysqld.Init(ctx, cnf, *initDBSQLFile); err != nil { return fmt.Errorf("failed init mysql: %v", err) } return nil @@ -81,13 +81,13 @@ func initCmd(subFlags *flag.FlagSet, args []string) error { func reinitConfigCmd(subFlags *flag.FlagSet, args []string) error { // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, err := mysqlctl.OpenMysqld(uint32(*tabletUID)) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(*tabletUID)) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } defer mysqld.Close() - if err := mysqld.ReinitConfig(context.TODO()); err != nil { + if err := mysqld.ReinitConfig(context.TODO(), cnf); err != nil { return fmt.Errorf("failed to reinit mysql config: %v", err) } return nil @@ -98,7 +98,7 @@ func shutdownCmd(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, err := mysqlctl.OpenMysqld(uint32(*tabletUID)) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(*tabletUID)) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } @@ -106,7 +106,7 @@ func shutdownCmd(subFlags *flag.FlagSet, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), *waitTime) defer cancel() - if err := mysqld.Shutdown(ctx, true); err != nil { + if err := mysqld.Shutdown(ctx, cnf, true); err != nil { return fmt.Errorf("failed shutdown mysql: %v", err) } return nil @@ -119,7 +119,7 @@ func startCmd(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, err := mysqlctl.OpenMysqld(uint32(*tabletUID)) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(*tabletUID)) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } @@ -127,7 +127,7 @@ func startCmd(subFlags *flag.FlagSet, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), *waitTime) defer cancel() - if err := mysqld.Start(ctx, mysqldArgs...); err != nil { + if err := mysqld.Start(ctx, cnf, mysqldArgs...); err != nil { return fmt.Errorf("failed start mysql: %v", err) } return nil @@ -139,7 +139,7 @@ func teardownCmd(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, err := mysqlctl.OpenMysqld(uint32(*tabletUID)) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(*tabletUID)) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } @@ -147,7 +147,7 @@ func teardownCmd(subFlags *flag.FlagSet, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), *waitTime) defer cancel() - if err := mysqld.Teardown(ctx, *force); err != nil { + if err := mysqld.Teardown(ctx, cnf, *force); err != nil { return fmt.Errorf("failed teardown mysql (forced? %v): %v", *force, err) } return nil diff --git a/go/cmd/mysqlctld/mysqlctld.go b/go/cmd/mysqlctld/mysqlctld.go index b304f9dcc71..6f9cebf296c 100644 --- a/go/cmd/mysqlctld/mysqlctld.go +++ b/go/cmd/mysqlctld/mysqlctld.go @@ -36,6 +36,7 @@ import ( var ( // mysqld is used by the rpc implementation plugin. mysqld *mysqlctl.Mysqld + cnf *mysqlctl.Mycnf mysqlPort = flag.Int("mysql_port", 3306, "mysql port") tabletUID = flag.Uint("tablet_uid", 41983, "tablet uid") @@ -74,14 +75,14 @@ func main() { log.Infof("mycnf file (%s) doesn't exist, initializing", mycnfFile) var err error - mysqld, err = mysqlctl.CreateMysqld(uint32(*tabletUID), *mysqlSocket, int32(*mysqlPort)) + mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(uint32(*tabletUID), *mysqlSocket, int32(*mysqlPort)) if err != nil { log.Errorf("failed to initialize mysql config: %v", err) exit.Return(1) } mysqld.OnTerm(onTermFunc) - if err := mysqld.Init(ctx, *initDBSQLFile); err != nil { + if err := mysqld.Init(ctx, cnf, *initDBSQLFile); err != nil { log.Errorf("failed to initialize mysql data dir and start mysqld: %v", err) exit.Return(1) } @@ -90,20 +91,20 @@ func main() { log.Infof("mycnf file (%s) already exists, starting without init", mycnfFile) var err error - mysqld, err = mysqlctl.OpenMysqld(uint32(*tabletUID)) + mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(uint32(*tabletUID)) if err != nil { log.Errorf("failed to find mysql config: %v", err) exit.Return(1) } mysqld.OnTerm(onTermFunc) - err = mysqld.RefreshConfig(ctx) + err = mysqld.RefreshConfig(ctx, cnf) if err != nil { log.Errorf("failed to refresh config: %v", err) exit.Return(1) } - if err := mysqld.Start(ctx); err != nil { + if err := mysqld.Start(ctx, cnf); err != nil { log.Errorf("failed to start mysqld: %v", err) exit.Return(1) } @@ -117,7 +118,7 @@ func main() { servenv.OnTermSync(func() { log.Infof("mysqlctl received SIGTERM, shutting down mysqld first") ctx := context.Background() - mysqld.Shutdown(ctx, true) + mysqld.Shutdown(ctx, cnf, true) }) // Start RPC server and wait for SIGTERM. diff --git a/go/cmd/mysqlctld/plugin_grpcmysqlctlserver.go b/go/cmd/mysqlctld/plugin_grpcmysqlctlserver.go index b14de1ef80f..088d1118c10 100644 --- a/go/cmd/mysqlctld/plugin_grpcmysqlctlserver.go +++ b/go/cmd/mysqlctld/plugin_grpcmysqlctlserver.go @@ -27,7 +27,7 @@ func init() { servenv.InitServiceMap("grpc", "mysqlctl") servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("mysqlctl") { - grpcmysqlctlserver.StartServer(servenv.GRPCServer, mysqld) + grpcmysqlctlserver.StartServer(servenv.GRPCServer, cnf, mysqld) } }) } diff --git a/go/cmd/vtcombo/main.go b/go/cmd/vtcombo/main.go index 9171f123638..3265b36c381 100644 --- a/go/cmd/vtcombo/main.go +++ b/go/cmd/vtcombo/main.go @@ -94,21 +94,16 @@ func main() { servenv.Init() tabletenv.Init() - // database configs - mycnf, err := mysqlctl.NewMycnfFromFlags(0) - if err != nil { - log.Errorf("mycnf read failed: %v", err) - exit.Return(1) - } - dbcfgs, err := dbconfigs.Init(mycnf.SocketFile) + dbcfgs, err := dbconfigs.Init("") if err != nil { log.Warning(err) } - mysqld := mysqlctl.NewMysqld(mycnf, dbcfgs) + mysqld := mysqlctl.NewMysqld(dbcfgs) servenv.OnClose(mysqld.Close) - // tablets configuration and init - if err := vtcombo.InitTabletMap(ts, tpb, mysqld, dbcfgs, *schemaDir, mycnf); err != nil { + // tablets configuration and init. + // Send mycnf as nil because vtcombo won't do backups and restores. + if err := vtcombo.InitTabletMap(ts, tpb, mysqld, dbcfgs, *schemaDir, nil); err != nil { log.Errorf("initTabletMapProto failed: %v", err) exit.Return(1) } diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index e96f7984604..f4ebedd9f5b 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -67,13 +67,30 @@ func main() { if err != nil { log.Exitf("failed to parse -tablet-path: %v", err) } + if tabletAlias.Uid < 0 { + log.Exitf("invalid tablet id: %d", tabletAlias.Uid) + } - mycnf, err := mysqlctl.NewMycnfFromFlags(tabletAlias.Uid) - if err != nil { - log.Exitf("mycnf read failed: %v", err) + var mycnf *mysqlctl.Mycnf + var socketFile string + // If no connection parameters were specified, load the mycnf file + // and use the socket from it. If connection parameters were specified, + // we assume that the mysql is not local, and we skip loading mycnf. + // This also means that backup and restore will not be allowed. + if !dbconfigs.HasConnectionParams() { + var err error + if mycnf, err = mysqlctl.NewMycnfFromFlags(tabletAlias.Uid); err != nil { + log.Exitf("mycnf read failed: %v", err) + } + socketFile = mycnf.SocketFile + } else { + log.Info("connection parameters were specified. Not loading my.cnf.") } - dbcfgs, err := dbconfigs.Init(mycnf.SocketFile) + // If connection parameters were specified, socketFile will be empty. + // Otherwise, the socketFile (read from mycnf) will be used to initialize + // dbconfigs. + dbcfgs, err := dbconfigs.Init(socketFile) if err != nil { log.Warning(err) } @@ -115,7 +132,7 @@ func main() { // Create mysqld and register the health reporter (needs to be done // before initializing the agent, so the initial health check // done by the agent has the right reporter) - mysqld := mysqlctl.NewMysqld(mycnf, dbcfgs) + mysqld := mysqlctl.NewMysqld(dbcfgs) servenv.OnClose(mysqld.Close) // Depends on both query and updateStream. diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index 33e4d6c49a9..802a5376c7f 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -214,6 +214,14 @@ func (dbcfgs *DBConfigs) Copy() *DBConfigs { return result } +// HasConnectionParams returns true if connection parameters were +// specified in the command-line. This will allow the caller to +// search for alternate ways to connect, like looking in the my.cnf +// file. +func HasConnectionParams() bool { + return baseConfig.Host != "" || baseConfig.UnixSocket != "" +} + // Init will initialize all the necessary connection parameters. // Precedence is as follows: if baseConfig command line options are // set, they supersede all other settings. @@ -222,17 +230,8 @@ func (dbcfgs *DBConfigs) Copy() *DBConfigs { // If no per-user parameters are supplied, then the defaultSocketFile // is used to initialize the per-user conn params. func Init(defaultSocketFile string) (*DBConfigs, error) { - // This is to support legacy behavior: use supplied socket value - // if conn parameters are not specified. - // TODO(sougou): deprecate. - for _, uc := range dbConfigs.userConfigs { - if uc.param.UnixSocket == "" && uc.param.Host == "" { - uc.param.UnixSocket = defaultSocketFile - } - } - // The new base configs, if set, supersede legacy settings. - if baseConfig.Host != "" || baseConfig.UnixSocket != "" { + if HasConnectionParams() { for _, uc := range dbConfigs.userConfigs { uc.param.Host = baseConfig.Host uc.param.Port = baseConfig.Port @@ -246,6 +245,13 @@ func Init(defaultSocketFile string) (*DBConfigs, error) { uc.param.SslKey = baseConfig.SslKey } } + } else { + // Use supplied socket value if conn parameters are not specified. + for _, uc := range dbConfigs.userConfigs { + if uc.param.UnixSocket == "" && uc.param.Host == "" { + uc.param.UnixSocket = defaultSocketFile + } + } } // See if the CredentialsServer is working. We do not use the diff --git a/go/vt/mysqlctl/backup.go b/go/vt/mysqlctl/backup.go index 1dfa5121707..87b100535cc 100644 --- a/go/vt/mysqlctl/backup.go +++ b/go/vt/mysqlctl/backup.go @@ -222,7 +222,7 @@ func findFilesToBackup(cnf *Mycnf) ([]FileEntry, error) { // - uses the BackupStorage service to store a new backup // - shuts down Mysqld during the backup // - remember if we were replicating, restore the exact same state -func Backup(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, dir, name string, backupConcurrency int, hookExtraEnv map[string]string) error { +func Backup(ctx context.Context, cnf *Mycnf, mysqld MysqlDaemon, logger logutil.Logger, dir, name string, backupConcurrency int, hookExtraEnv map[string]string) error { // Start the backup with the BackupStorage. bs, err := backupstorage.GetBackupStorage() if err != nil { @@ -235,7 +235,7 @@ func Backup(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, dir, } // Take the backup, and either AbortBackup or EndBackup. - usable, err := backup(ctx, mysqld, logger, bh, backupConcurrency, hookExtraEnv) + usable, err := backup(ctx, cnf, mysqld, logger, bh, backupConcurrency, hookExtraEnv) var finishErr error if usable { finishErr = bh.EndBackup(ctx) @@ -259,7 +259,7 @@ func Backup(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, dir, // backup returns a boolean that indicates if the backup is usable, // and an overall error. -func backup(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, bh backupstorage.BackupHandle, backupConcurrency int, hookExtraEnv map[string]string) (bool, error) { +func backup(ctx context.Context, cnf *Mycnf, mysqld MysqlDaemon, logger logutil.Logger, bh backupstorage.BackupHandle, backupConcurrency int, hookExtraEnv map[string]string) (bool, error) { // Save initial state so we can restore. slaveStartRequired := false sourceIsMaster := false @@ -312,22 +312,22 @@ func backup(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, bh b logger.Infof("using replication position: %v", replicationPosition) // shutdown mysqld - err = mysqld.Shutdown(ctx, true) + err = mysqld.Shutdown(ctx, cnf, true) if err != nil { return false, fmt.Errorf("can't shutdown mysqld: %v", err) } // Backup everything, capture the error. - backupErr := backupFiles(ctx, mysqld, logger, bh, replicationPosition, backupConcurrency, hookExtraEnv) + backupErr := backupFiles(ctx, cnf, mysqld, logger, bh, replicationPosition, backupConcurrency, hookExtraEnv) usable := backupErr == nil // Try to restart mysqld - err = mysqld.RefreshConfig(ctx) + err = mysqld.RefreshConfig(ctx, cnf) if err != nil { return usable, fmt.Errorf("can't refresh mysqld config: %v", err) } - err = mysqld.Start(ctx) + err = mysqld.Start(ctx, cnf) if err != nil { return usable, fmt.Errorf("can't restart mysqld: %v", err) } @@ -365,9 +365,9 @@ func backup(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, bh b } // backupFiles finds the list of files to backup, and creates the backup. -func backupFiles(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, bh backupstorage.BackupHandle, replicationPosition mysql.Position, backupConcurrency int, hookExtraEnv map[string]string) (err error) { +func backupFiles(ctx context.Context, cnf *Mycnf, mysqld MysqlDaemon, logger logutil.Logger, bh backupstorage.BackupHandle, replicationPosition mysql.Position, backupConcurrency int, hookExtraEnv map[string]string) (err error) { // Get the files to backup. - fes, err := findFilesToBackup(mysqld.Cnf()) + fes, err := findFilesToBackup(cnf) if err != nil { return fmt.Errorf("can't find files to backup: %v", err) } @@ -392,7 +392,7 @@ func backupFiles(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, // Backup the individual file. name := fmt.Sprintf("%v", i) - rec.RecordError(backupFile(ctx, mysqld, logger, bh, &fes[i], name, hookExtraEnv)) + rec.RecordError(backupFile(ctx, cnf, mysqld, logger, bh, &fes[i], name, hookExtraEnv)) }(i) } @@ -431,10 +431,10 @@ func backupFiles(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, } // backupFile backs up an individual file. -func backupFile(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, bh backupstorage.BackupHandle, fe *FileEntry, name string, hookExtraEnv map[string]string) (err error) { +func backupFile(ctx context.Context, cnf *Mycnf, mysqld MysqlDaemon, logger logutil.Logger, bh backupstorage.BackupHandle, fe *FileEntry, name string, hookExtraEnv map[string]string) (err error) { // Open the source file for reading. var source *os.File - source, err = fe.open(mysqld.Cnf(), true) + source, err = fe.open(cnf, true) if err != nil { return err } @@ -734,6 +734,7 @@ func removeExistingFiles(cnf *Mycnf) error { // and returns ErrNoBackup. Any other error is returned. func Restore( ctx context.Context, + cnf *Mycnf, mysqld MysqlDaemon, dir string, restoreConcurrency int, @@ -744,7 +745,7 @@ func Restore( dbName string) (mysql.Position, error) { // Wait for mysqld to be ready, in case it was launched in parallel with us. - if err := mysqld.Wait(ctx); err != nil { + if err := mysqld.Wait(ctx, cnf); err != nil { return mysql.Position{}, err } @@ -817,24 +818,24 @@ func Restore( // context. Thus we use the background context to get through to the finish. logger.Infof("Restore: shutdown mysqld") - err = mysqld.Shutdown(context.Background(), true) + err = mysqld.Shutdown(context.Background(), cnf, true) if err != nil { return mysql.Position{}, err } logger.Infof("Restore: deleting existing files") - if err := removeExistingFiles(mysqld.Cnf()); err != nil { + if err := removeExistingFiles(cnf); err != nil { return mysql.Position{}, err } logger.Infof("Restore: reinit config file") - err = mysqld.ReinitConfig(context.Background()) + err = mysqld.ReinitConfig(context.Background(), cnf) if err != nil { return mysql.Position{}, err } logger.Infof("Restore: copying all files") - if err := restoreFiles(context.Background(), mysqld.Cnf(), bh, bm.FileEntries, bm.TransformHook, !bm.SkipCompress, restoreConcurrency, hookExtraEnv); err != nil { + if err := restoreFiles(context.Background(), cnf, bh, bm.FileEntries, bm.TransformHook, !bm.SkipCompress, restoreConcurrency, hookExtraEnv); err != nil { return mysql.Position{}, err } @@ -847,7 +848,7 @@ func Restore( // of those who can connect. logger.Infof("Restore: starting mysqld for mysql_upgrade") // Note Start will use dba user for waiting, this is fine, it will be allowed. - err = mysqld.Start(context.Background(), "--skip-grant-tables", "--skip-networking") + err = mysqld.Start(context.Background(), cnf, "--skip-grant-tables", "--skip-networking") if err != nil { return mysql.Position{}, err } @@ -868,11 +869,11 @@ func Restore( // The MySQL manual recommends restarting mysqld after running mysql_upgrade, // so that any changes made to system tables take effect. logger.Infof("Restore: restarting mysqld after mysql_upgrade") - err = mysqld.Shutdown(context.Background(), true) + err = mysqld.Shutdown(context.Background(), cnf, true) if err != nil { return mysql.Position{}, err } - err = mysqld.Start(context.Background()) + err = mysqld.Start(context.Background(), cnf) if err != nil { return mysql.Position{}, err } diff --git a/go/vt/mysqlctl/cmd.go b/go/vt/mysqlctl/cmd.go index ab5684d0b81..7289d999570 100644 --- a/go/vt/mysqlctl/cmd.go +++ b/go/vt/mysqlctl/cmd.go @@ -26,10 +26,9 @@ import ( "vitess.io/vitess/go/vt/dbconfigs" ) -// CreateMysqld returns a Mysqld object to use for working with a MySQL -// installation that hasn't been set up yet. This will generate a new my.cnf -// from scratch and use that to call NewMysqld(). -func CreateMysqld(tabletUID uint32, mysqlSocket string, mysqlPort int32) (*Mysqld, error) { +// CreateMysqldAndMycnf returns a Mysqld and a Mycnf object to use for working with a MySQL +// installation that hasn't been set up yet. +func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int32) (*Mysqld, *Mycnf, error) { mycnf := NewMycnf(tabletUID, mysqlPort) // Choose a random MySQL server-id, since this is a fresh data dir. // We don't want to use the tablet UID as the MySQL server-id, @@ -41,7 +40,7 @@ func CreateMysqld(tabletUID uint32, mysqlSocket string, mysqlPort int32) (*Mysql // lose data by skipping binlog events due to replicate-same-server-id=FALSE, // which is the default setting. if err := mycnf.RandomizeMysqlServerID(); err != nil { - return nil, fmt.Errorf("couldn't generate random MySQL server_id: %v", err) + return nil, nil, fmt.Errorf("couldn't generate random MySQL server_id: %v", err) } if mysqlSocket != "" { mycnf.SocketFile = mysqlSocket @@ -49,26 +48,26 @@ func CreateMysqld(tabletUID uint32, mysqlSocket string, mysqlPort int32) (*Mysql dbcfgs, err := dbconfigs.Init(mycnf.SocketFile) if err != nil { - return nil, fmt.Errorf("couldn't Init dbconfigs: %v", err) + return nil, nil, fmt.Errorf("couldn't Init dbconfigs: %v", err) } - return NewMysqld(mycnf, dbcfgs), nil + return NewMysqld(dbcfgs), mycnf, nil } -// OpenMysqld returns a Mysqld object to use for working with a MySQL -// installation that already exists. This will look for an existing my.cnf file -// and use that to call NewMysqld(). -func OpenMysqld(tabletUID uint32) (*Mysqld, error) { +// OpenMysqldAndMycnf returns a Mysqld and a Mycnf object to use for working with a MySQL +// installation that already exists. The Mycnf will be built based on the my.cnf file +// of the MySQL instance. +func OpenMysqldAndMycnf(tabletUID uint32) (*Mysqld, *Mycnf, error) { // We pass a port of 0, this will be read and overwritten from the path on disk mycnf, err := ReadMycnf(NewMycnf(tabletUID, 0)) if err != nil { - return nil, fmt.Errorf("couldn't read my.cnf file: %v", err) + return nil, nil, fmt.Errorf("couldn't read my.cnf file: %v", err) } dbcfgs, err := dbconfigs.Init(mycnf.SocketFile) if err != nil { - return nil, fmt.Errorf("couldn't Init dbconfigs: %v", err) + return nil, nil, fmt.Errorf("couldn't Init dbconfigs: %v", err) } - return NewMysqld(mycnf, dbcfgs), nil + return NewMysqld(dbcfgs), mycnf, nil } diff --git a/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go b/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go index dcc63a13f75..3869933f8af 100644 --- a/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go +++ b/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go @@ -44,9 +44,6 @@ type FakeMysqlDaemon struct { // appPool is set if db is set. appPool *dbconnpool.ConnectionPool - // Mycnf will be returned by Cnf() - Mycnf *mysqlctl.Mycnf - // Running is used by Start / Shutdown Running bool @@ -151,18 +148,8 @@ func NewFakeMysqlDaemon(db *fakesqldb.DB) *FakeMysqlDaemon { return result } -// Cnf is part of the MysqlDaemon interface -func (fmd *FakeMysqlDaemon) Cnf() *mysqlctl.Mycnf { - return fmd.Mycnf -} - -// TabletDir is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) TabletDir() string { - return "" -} - // Start is part of the MysqlDaemon interface -func (fmd *FakeMysqlDaemon) Start(ctx context.Context, mysqldArgs ...string) error { +func (fmd *FakeMysqlDaemon) Start(ctx context.Context, cnf *mysqlctl.Mycnf, mysqldArgs ...string) error { if fmd.Running { return fmt.Errorf("fake mysql daemon already running") } @@ -171,7 +158,7 @@ func (fmd *FakeMysqlDaemon) Start(ctx context.Context, mysqldArgs ...string) err } // Shutdown is part of the MysqlDaemon interface -func (fmd *FakeMysqlDaemon) Shutdown(ctx context.Context, waitForMysqld bool) error { +func (fmd *FakeMysqlDaemon) Shutdown(ctx context.Context, cnf *mysqlctl.Mycnf, waitForMysqld bool) error { if !fmd.Running { return fmt.Errorf("fake mysql daemon not running") } @@ -185,17 +172,17 @@ func (fmd *FakeMysqlDaemon) RunMysqlUpgrade() error { } // ReinitConfig is part of the MysqlDaemon interface -func (fmd *FakeMysqlDaemon) ReinitConfig(ctx context.Context) error { +func (fmd *FakeMysqlDaemon) ReinitConfig(ctx context.Context, cnf *mysqlctl.Mycnf) error { return nil } // RefreshConfig is part of the MysqlDaemon interface -func (fmd *FakeMysqlDaemon) RefreshConfig(ctx context.Context) error { +func (fmd *FakeMysqlDaemon) RefreshConfig(ctx context.Context, cnf *mysqlctl.Mycnf) error { return nil } // Wait is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) Wait(ctx context.Context) error { +func (fmd *FakeMysqlDaemon) Wait(ctx context.Context, cnf *mysqlctl.Mycnf) error { return nil } diff --git a/go/vt/mysqlctl/grpcmysqlctlserver/server.go b/go/vt/mysqlctl/grpcmysqlctlserver/server.go index 340e7fc3277..72be321923a 100644 --- a/go/vt/mysqlctl/grpcmysqlctlserver/server.go +++ b/go/vt/mysqlctl/grpcmysqlctlserver/server.go @@ -31,17 +31,18 @@ import ( // server is our gRPC server. type server struct { + cnf *mysqlctl.Mycnf mysqld *mysqlctl.Mysqld } // Start implements the server side of the MysqlctlClient interface. func (s *server) Start(ctx context.Context, request *mysqlctlpb.StartRequest) (*mysqlctlpb.StartResponse, error) { - return &mysqlctlpb.StartResponse{}, s.mysqld.Start(ctx, request.MysqldArgs...) + return &mysqlctlpb.StartResponse{}, s.mysqld.Start(ctx, s.cnf, request.MysqldArgs...) } // Shutdown implements the server side of the MysqlctlClient interface. func (s *server) Shutdown(ctx context.Context, request *mysqlctlpb.ShutdownRequest) (*mysqlctlpb.ShutdownResponse, error) { - return &mysqlctlpb.ShutdownResponse{}, s.mysqld.Shutdown(ctx, request.WaitForMysqld) + return &mysqlctlpb.ShutdownResponse{}, s.mysqld.Shutdown(ctx, s.cnf, request.WaitForMysqld) } // RunMysqlUpgrade implements the server side of the MysqlctlClient interface. @@ -51,15 +52,15 @@ func (s *server) RunMysqlUpgrade(ctx context.Context, request *mysqlctlpb.RunMys // ReinitConfig implements the server side of the MysqlctlClient interface. func (s *server) ReinitConfig(ctx context.Context, request *mysqlctlpb.ReinitConfigRequest) (*mysqlctlpb.ReinitConfigResponse, error) { - return &mysqlctlpb.ReinitConfigResponse{}, s.mysqld.ReinitConfig(ctx) + return &mysqlctlpb.ReinitConfigResponse{}, s.mysqld.ReinitConfig(ctx, s.cnf) } // RefreshConfig implements the server side of the MysqlctlClient interface. func (s *server) RefreshConfig(ctx context.Context, request *mysqlctlpb.RefreshConfigRequest) (*mysqlctlpb.RefreshConfigResponse, error) { - return &mysqlctlpb.RefreshConfigResponse{}, s.mysqld.RefreshConfig(ctx) + return &mysqlctlpb.RefreshConfigResponse{}, s.mysqld.RefreshConfig(ctx, s.cnf) } // StartServer registers the Server for RPCs. -func StartServer(s *grpc.Server, mysqld *mysqlctl.Mysqld) { - mysqlctlpb.RegisterMysqlCtlServer(s, &server{mysqld}) +func StartServer(s *grpc.Server, cnf *mysqlctl.Mycnf, mysqld *mysqlctl.Mysqld) { + mysqlctlpb.RegisterMysqlCtlServer(s, &server{cnf: cnf, mysqld: mysqld}) } diff --git a/go/vt/mysqlctl/mycnf.go b/go/vt/mysqlctl/mycnf.go index 3adba0728e1..f4dae766bbb 100644 --- a/go/vt/mysqlctl/mycnf.go +++ b/go/vt/mysqlctl/mycnf.go @@ -26,6 +26,7 @@ import ( "fmt" "io" "os" + "path" "strconv" ) @@ -107,6 +108,11 @@ type Mycnf struct { path string // the actual path that represents this mycnf } +// TabletDir returns the tablet directory. +func (cnf *Mycnf) TabletDir() string { + return path.Dir(cnf.DataDir) +} + func (cnf *Mycnf) lookup(key string) string { key = normKey([]byte(key)) return cnf.mycnfMap[key] diff --git a/go/vt/mysqlctl/mycnf_flag.go b/go/vt/mysqlctl/mycnf_flag.go index 031d0869708..024be07403d 100644 --- a/go/vt/mysqlctl/mycnf_flag.go +++ b/go/vt/mysqlctl/mycnf_flag.go @@ -119,9 +119,6 @@ func NewMycnfFromFlags(uid uint32) (mycnf *Mycnf, err error) { } if *flagMycnfFile == "" { - if uid == 0 { - log.Exitf("No mycnf_server_id, no mycnf-file, and no backup server id to use") - } *flagMycnfFile = MycnfFile(uid) log.Infof("No mycnf_server_id, no mycnf-file specified, using default config for server id %v: %v", uid, *flagMycnfFile) } else { diff --git a/go/vt/mysqlctl/mysql_daemon.go b/go/vt/mysqlctl/mysql_daemon.go index 8828eb28782..2ee5b3e7bff 100644 --- a/go/vt/mysqlctl/mysql_daemon.go +++ b/go/vt/mysqlctl/mysql_daemon.go @@ -29,18 +29,13 @@ import ( // MysqlDaemon is the interface we use for abstracting Mysqld. type MysqlDaemon interface { - // Cnf returns the underlying mycnf - Cnf() *Mycnf - // TabletDir returns the tablet directory. - TabletDir() string - // methods related to mysql running or not - Start(ctx context.Context, mysqldArgs ...string) error - Shutdown(ctx context.Context, waitForMysqld bool) error + Start(ctx context.Context, cnf *Mycnf, mysqldArgs ...string) error + Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool) error RunMysqlUpgrade() error - ReinitConfig(ctx context.Context) error - RefreshConfig(ctx context.Context) error - Wait(ctx context.Context) error + ReinitConfig(ctx context.Context, cnf *Mycnf) error + RefreshConfig(ctx context.Context, cnf *Mycnf) error + Wait(ctx context.Context, cnf *Mycnf) error // GetMysqlPort returns the current port mysql is listening on. GetMysqlPort() (int32, error) diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index 900f8f9ee2c..46c2acf6fe1 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -80,11 +80,9 @@ var ( // Mysqld is the object that represents a mysqld daemon running on this server. type Mysqld struct { - config *Mycnf - dbcfgs *dbconfigs.DBConfigs - dbaPool *dbconnpool.ConnectionPool - appPool *dbconnpool.ConnectionPool - tabletDir string + dbcfgs *dbconfigs.DBConfigs + dbaPool *dbconnpool.ConnectionPool + appPool *dbconnpool.ConnectionPool // mutex protects the fields below. mutex sync.Mutex @@ -94,11 +92,9 @@ type Mysqld struct { // NewMysqld creates a Mysqld object based on the provided configuration // and connection parameters. -func NewMysqld(config *Mycnf, dbcfgs *dbconfigs.DBConfigs) *Mysqld { +func NewMysqld(dbcfgs *dbconfigs.DBConfigs) *Mysqld { result := &Mysqld{ - config: config, - dbcfgs: dbcfgs, - tabletDir: path.Dir(config.DataDir), + dbcfgs: dbcfgs, } // Create and open the connection pool for dba access. @@ -112,17 +108,6 @@ func NewMysqld(config *Mycnf, dbcfgs *dbconfigs.DBConfigs) *Mysqld { return result } -// Cnf returns the mysql config for the daemon -func (mysqld *Mysqld) Cnf() *Mycnf { - return mysqld.config -} - -// TabletDir returns the main tablet directory. -// It's a method so it can be accessed through the MysqlDaemon interface. -func (mysqld *Mysqld) TabletDir() string { - return mysqld.tabletDir -} - // 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. @@ -160,18 +145,18 @@ func (mysqld *Mysqld) RunMysqlUpgrade() error { if err != nil { return err } - cnf, err := mysqld.defaultsExtraFile(params) + defaultsFile, err := mysqld.defaultsExtraFile(params) if err != nil { return err } - defer os.Remove(cnf) + defer os.Remove(defaultsFile) // Run the program, if it fails, we fail. Note in this // moment, mysqld is running with no grant tables on the local // socket only, so this doesn't need any user or password. args := []string{ // --defaults-file=* must be the first arg. - "--defaults-file=" + cnf, + "--defaults-file=" + defaultsFile, "--force", // Don't complain if it's already been upgraded. } cmd := exec.Command(name, args...) @@ -186,7 +171,7 @@ func (mysqld *Mysqld) RunMysqlUpgrade() error { // If a mysqlctld address is provided in a flag, Start will run // remotely. When waiting for mysqld to start, we will use // the dba user. -func (mysqld *Mysqld) Start(ctx context.Context, mysqldArgs ...string) error { +func (mysqld *Mysqld) Start(ctx context.Context, cnf *Mycnf, mysqldArgs ...string) error { // Execute as remote action on mysqlctld if requested. if *socketFile != "" { log.Infof("executing Mysqld.Start() remotely via mysqlctld server: %v", *socketFile) @@ -198,15 +183,15 @@ func (mysqld *Mysqld) Start(ctx context.Context, mysqldArgs ...string) error { return client.Start(ctx, mysqldArgs...) } - if err := mysqld.startNoWait(ctx, mysqldArgs...); err != nil { + if err := mysqld.startNoWait(ctx, cnf, mysqldArgs...); err != nil { return err } - return mysqld.Wait(ctx) + return mysqld.Wait(ctx, cnf) } // startNoWait is the internal version of Start, and it doesn't wait. -func (mysqld *Mysqld) startNoWait(ctx context.Context, mysqldArgs ...string) error { +func (mysqld *Mysqld) startNoWait(ctx context.Context, cnf *Mycnf, mysqldArgs ...string) error { var name string ts := fmt.Sprintf("Mysqld.Start(%v)", time.Now().Unix()) @@ -234,7 +219,7 @@ func (mysqld *Mysqld) startNoWait(ctx context.Context, mysqldArgs ...string) err } } arg := []string{ - "--defaults-file=" + mysqld.config.path} + "--defaults-file=" + cnf.path} arg = append(arg, mysqldArgs...) env := []string{os.ExpandEnv("LD_LIBRARY_PATH=$VT_MYSQL_ROOT/lib/mysql")} @@ -297,27 +282,27 @@ func (mysqld *Mysqld) startNoWait(ctx context.Context, mysqldArgs ...string) err // Wait returns nil when mysqld is up and accepting connections. It // will use the dba credentials to try to connect. Use wait() with // different credentials if needed. -func (mysqld *Mysqld) Wait(ctx context.Context) error { +func (mysqld *Mysqld) Wait(ctx context.Context, cnf *Mycnf) error { params, err := dbconfigs.WithCredentials(mysqld.dbcfgs.Dba()) if err != nil { return err } - return mysqld.wait(ctx, params) + return mysqld.wait(ctx, cnf, params) } // wait is the internal version of Wait, that takes credentials. -func (mysqld *Mysqld) wait(ctx context.Context, params *mysql.ConnParams) error { - log.Infof("Waiting for mysqld socket file (%v) to be ready...", mysqld.config.SocketFile) +func (mysqld *Mysqld) wait(ctx context.Context, cnf *Mycnf, params *mysql.ConnParams) error { + log.Infof("Waiting for mysqld socket file (%v) to be ready...", cnf.SocketFile) for { select { case <-ctx.Done(): - return errors.New("deadline exceeded waiting for mysqld socket file to appear: " + mysqld.config.SocketFile) + return errors.New("deadline exceeded waiting for mysqld socket file to appear: " + cnf.SocketFile) default: } - _, statErr := os.Stat(mysqld.config.SocketFile) + _, statErr := os.Stat(cnf.SocketFile) if statErr == nil { // Make sure the socket file isn't stale. conn, connErr := mysql.Connect(ctx, params) @@ -340,7 +325,7 @@ func (mysqld *Mysqld) wait(ctx context.Context, params *mysql.ConnParams) error // flushed - on the order of 20-30 minutes. // // If a mysqlctld address is provided in a flag, Shutdown will run remotely. -func (mysqld *Mysqld) Shutdown(ctx context.Context, waitForMysqld bool) error { +func (mysqld *Mysqld) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool) error { log.Infof("Mysqld.Shutdown") // Execute as remote action on mysqlctld if requested. @@ -364,8 +349,8 @@ func (mysqld *Mysqld) Shutdown(ctx context.Context, waitForMysqld bool) error { mysqld.mutex.Unlock() // possibly mysql is already shutdown, check for a few files first - _, socketPathErr := os.Stat(mysqld.config.SocketFile) - _, pidPathErr := os.Stat(mysqld.config.PidFile) + _, socketPathErr := os.Stat(cnf.SocketFile) + _, pidPathErr := os.Stat(cnf.PidFile) if os.IsNotExist(socketPathErr) && os.IsNotExist(pidPathErr) { log.Warningf("assuming mysqld already shut down - no socket, no pid file found") return nil @@ -421,7 +406,7 @@ func (mysqld *Mysqld) Shutdown(ctx context.Context, waitForMysqld bool) error { // didn't start. if waitForMysqld { log.Infof("Mysqld.Shutdown: waiting for socket file (%v) and pid file (%v) to disappear", - mysqld.config.SocketFile, mysqld.config.PidFile) + cnf.SocketFile, cnf.PidFile) for { select { @@ -430,8 +415,8 @@ func (mysqld *Mysqld) Shutdown(ctx context.Context, waitForMysqld bool) error { default: } - _, socketPathErr = os.Stat(mysqld.config.SocketFile) - _, pidPathErr = os.Stat(mysqld.config.PidFile) + _, socketPathErr = os.Stat(cnf.SocketFile) + _, pidPathErr = os.Stat(cnf.PidFile) if os.IsNotExist(socketPathErr) && os.IsNotExist(pidPathErr) { return nil } @@ -479,9 +464,9 @@ func binaryPath(root, binary string) (string, error) { // InitConfig will create the default directory structure for the mysqld process, // generate / configure a my.cnf file. -func (mysqld *Mysqld) InitConfig() error { +func (mysqld *Mysqld) InitConfig(cnf *Mycnf) error { log.Infof("mysqlctl.InitConfig") - err := mysqld.createDirs() + err := mysqld.createDirs(cnf) if err != nil { log.Errorf("%s", err.Error()) return err @@ -493,8 +478,8 @@ func (mysqld *Mysqld) InitConfig() error { } // Set up config files. - if err = mysqld.initConfig(root, mysqld.config.path); err != nil { - log.Errorf("failed creating %v: %v", mysqld.config.path, err) + if err = mysqld.initConfig(root, cnf, cnf.path); err != nil { + log.Errorf("failed creating %v: %v", cnf.path, err) return err } return nil @@ -503,22 +488,22 @@ func (mysqld *Mysqld) InitConfig() error { // Init will create the default directory structure for the mysqld process, // generate / configure a my.cnf file install a skeleton database, // and apply the provided initial SQL file. -func (mysqld *Mysqld) Init(ctx context.Context, initDBSQLFile string) error { +func (mysqld *Mysqld) Init(ctx context.Context, cnf *Mycnf, initDBSQLFile string) error { log.Infof("mysqlctl.Init") - err := mysqld.InitConfig() + err := mysqld.InitConfig(cnf) if err != nil { log.Errorf("%s", err.Error()) return err } // Install data dir. - if err = mysqld.installDataDir(); err != nil { + if err = mysqld.installDataDir(cnf); err != nil { return err } // Start mysqld. We do not use Start, as we have to wait using // the root user. - if err = mysqld.startNoWait(ctx); err != nil { - log.Errorf("failed starting mysqld (check mysql error log %v for more info): %v", mysqld.config.ErrorLogPath, err) + if err = mysqld.startNoWait(ctx, cnf); err != nil { + log.Errorf("failed starting mysqld (check mysql error log %v for more info): %v", cnf.ErrorLogPath, err) return err } @@ -527,10 +512,10 @@ func (mysqld *Mysqld) Init(ctx context.Context, initDBSQLFile string) error { params := &mysql.ConnParams{ Uname: "root", Charset: "utf8", - UnixSocket: mysqld.config.SocketFile, + UnixSocket: cnf.SocketFile, } - if err = mysqld.wait(ctx, params); err != nil { - log.Errorf("failed starting mysqld in time (check mysyql error log %v for more info): %v", mysqld.config.ErrorLogPath, err) + if err = mysqld.wait(ctx, cnf, params); err != nil { + log.Errorf("failed starting mysqld in time (check mysyql error log %v for more info): %v", cnf.ErrorLogPath, err) return err } @@ -554,7 +539,7 @@ func useMysqldInitialize(version string) bool { strings.Contains(version, "Ver 8.0.") } -func (mysqld *Mysqld) installDataDir() error { +func (mysqld *Mysqld) installDataDir(cnf *Mycnf) error { mysqlRoot, err := vtenv.VtMysqlRoot() if err != nil { return err @@ -579,7 +564,7 @@ func (mysqld *Mysqld) installDataDir() error { log.Infof("Installing data dir with mysqld --initialize-insecure") args := []string{ - "--defaults-file=" + mysqld.config.path, + "--defaults-file=" + cnf.path, "--basedir=" + mysqlBaseDir, "--initialize-insecure", // Use empty 'root'@'localhost' password. } @@ -592,7 +577,7 @@ func (mysqld *Mysqld) installDataDir() error { log.Infof("Installing data dir with mysql_install_db") args := []string{ - "--defaults-file=" + mysqld.config.path, + "--defaults-file=" + cnf.path, "--basedir=" + mysqlBaseDir, } cmdPath, err := binaryPath(mysqlRoot, "mysql_install_db") @@ -606,16 +591,16 @@ func (mysqld *Mysqld) installDataDir() error { return nil } -func (mysqld *Mysqld) initConfig(root, outFile string) error { +func (mysqld *Mysqld) initConfig(root string, cnf *Mycnf, outFile string) error { var err error var configData string switch hr := hook.NewSimpleHook("make_mycnf").Execute(); hr.ExitStatus { case hook.HOOK_DOES_NOT_EXIST: log.Infof("make_mycnf hook doesn't exist, reading template files") - configData, err = mysqld.config.makeMycnf(getMycnfTemplates(root)) + configData, err = cnf.makeMycnf(getMycnfTemplates(root)) case hook.HOOK_SUCCESS: - configData, err = mysqld.config.fillMycnfTemplate(hr.Stdout) + configData, err = cnf.fillMycnfTemplate(hr.Stdout) default: return fmt.Errorf("make_mycnf hook failed(%v): %v", hr.ExitStatus, hr.Stderr) } @@ -648,7 +633,7 @@ func getMycnfTemplates(root string) []string { // RefreshConfig attempts to recreate the my.cnf from templates, and log and // swap in to place if it's updated. It keeps a copy of the last version in case fallback is required. // Should be called from a stable replica, server_id is not regenerated. -func (mysqld *Mysqld) RefreshConfig(ctx context.Context) error { +func (mysqld *Mysqld) RefreshConfig(ctx context.Context, cnf *Mycnf) error { // Execute as remote action on mysqlctld if requested. if *socketFile != "" { log.Infof("executing Mysqld.RefreshConfig() remotely via mysqlctld server: %v", *socketFile) @@ -665,20 +650,20 @@ func (mysqld *Mysqld) RefreshConfig(ctx context.Context) error { if err != nil { return err } - f, err := ioutil.TempFile(path.Dir(mysqld.config.path), "my.cnf") + f, err := ioutil.TempFile(path.Dir(cnf.path), "my.cnf") if err != nil { return fmt.Errorf("Could not create temp file: %v", err) } defer os.Remove(f.Name()) - err = mysqld.initConfig(root, f.Name()) + err = mysqld.initConfig(root, cnf, f.Name()) if err != nil { return fmt.Errorf("Could not initConfig in %v: %v", f.Name(), err) } - existing, err := ioutil.ReadFile(mysqld.config.path) + existing, err := ioutil.ReadFile(cnf.path) if err != nil { - return fmt.Errorf("Could not read existing file %v: %v", mysqld.config.path, err) + return fmt.Errorf("Could not read existing file %v: %v", cnf.path, err) } updated, err := ioutil.ReadFile(f.Name()) if err != nil { @@ -690,14 +675,14 @@ func (mysqld *Mysqld) RefreshConfig(ctx context.Context) error { return nil } - backupPath := mysqld.config.path + ".previous" - err = os.Rename(mysqld.config.path, backupPath) + backupPath := cnf.path + ".previous" + err = os.Rename(cnf.path, backupPath) if err != nil { - return fmt.Errorf("Could not back up existing %v: %v", mysqld.config.path, err) + return fmt.Errorf("Could not back up existing %v: %v", cnf.path, err) } - err = os.Rename(f.Name(), mysqld.config.path) + err = os.Rename(f.Name(), cnf.path) if err != nil { - return fmt.Errorf("Could not move %v to %v: %v", f.Name(), mysqld.config.path, err) + return fmt.Errorf("Could not move %v to %v: %v", f.Name(), cnf.path, err) } log.Infof("Updated my.cnf. Backup of previous version available in %v", backupPath) @@ -708,7 +693,7 @@ func (mysqld *Mysqld) RefreshConfig(ctx context.Context) error { // moment it only randomizes ServerID because it's not safe to restore a replica // from a backup and then give it the same ServerID as before, MySQL can then // skip transactions in the replication stream with the same server_id. -func (mysqld *Mysqld) ReinitConfig(ctx context.Context) error { +func (mysqld *Mysqld) ReinitConfig(ctx context.Context, cnf *Mycnf) error { log.Infof("Mysqld.ReinitConfig") // Execute as remote action on mysqlctld if requested. @@ -722,27 +707,28 @@ func (mysqld *Mysqld) ReinitConfig(ctx context.Context) error { return client.ReinitConfig(ctx) } - if err := mysqld.config.RandomizeMysqlServerID(); err != nil { + if err := cnf.RandomizeMysqlServerID(); err != nil { return err } root, err := vtenv.VtRoot() if err != nil { return err } - return mysqld.initConfig(root, mysqld.config.path) + return mysqld.initConfig(root, cnf, cnf.path) } -func (mysqld *Mysqld) createDirs() error { - log.Infof("creating directory %s", mysqld.tabletDir) - if err := os.MkdirAll(mysqld.tabletDir, os.ModePerm); err != nil { +func (mysqld *Mysqld) createDirs(cnf *Mycnf) error { + tabletDir := cnf.TabletDir() + log.Infof("creating directory %s", tabletDir) + if err := os.MkdirAll(tabletDir, os.ModePerm); err != nil { return err } for _, dir := range TopLevelDirs() { - if err := mysqld.createTopDir(dir); err != nil { + if err := mysqld.createTopDir(cnf, dir); err != nil { return err } } - for _, dir := range mysqld.config.directoryList() { + for _, dir := range cnf.directoryList() { log.Infof("creating directory %s", dir) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return err @@ -759,20 +745,21 @@ func (mysqld *Mysqld) createDirs() error { // that points to the newly created directory. For example, if // /vt/data is present, it will create the following structure: // /vt/data/vt_xxxx /vt/vt_xxxx/data -> /vt/data/vt_xxxx -func (mysqld *Mysqld) createTopDir(dir string) error { - vtname := path.Base(mysqld.tabletDir) +func (mysqld *Mysqld) createTopDir(cnf *Mycnf, dir string) error { + tabletDir := cnf.TabletDir() + vtname := path.Base(tabletDir) target := path.Join(vtenv.VtDataRoot(), dir) _, err := os.Lstat(target) if err != nil { if os.IsNotExist(err) { - topdir := path.Join(mysqld.tabletDir, dir) + topdir := path.Join(tabletDir, dir) log.Infof("creating directory %s", topdir) return os.MkdirAll(topdir, os.ModePerm) } return err } linkto := path.Join(target, vtname) - source := path.Join(mysqld.tabletDir, dir) + source := path.Join(tabletDir, dir) log.Infof("creating directory %s", linkto) err = os.MkdirAll(linkto, os.ModePerm) if err != nil { @@ -783,9 +770,9 @@ func (mysqld *Mysqld) createTopDir(dir string) error { } // Teardown will shutdown the running daemon, and delete the root directory. -func (mysqld *Mysqld) Teardown(ctx context.Context, force bool) error { +func (mysqld *Mysqld) Teardown(ctx context.Context, cnf *Mycnf, force bool) error { log.Infof("mysqlctl.Teardown") - if err := mysqld.Shutdown(ctx, true); err != nil { + if err := mysqld.Shutdown(ctx, cnf, true); err != nil { log.Warningf("failed mysqld shutdown: %v", err.Error()) if !force { return err @@ -793,7 +780,7 @@ func (mysqld *Mysqld) Teardown(ctx context.Context, force bool) error { } var removalErr error for _, dir := range TopLevelDirs() { - qdir := path.Join(mysqld.tabletDir, dir) + qdir := path.Join(cnf.TabletDir(), dir) if err := deleteTopDir(qdir); err != nil { removalErr = err } diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 79e6dae5600..4abe92269ef 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -87,6 +87,7 @@ type ActionAgent struct { HealthReporter health.Reporter TopoServer *topo.Server TabletAlias *topodatapb.TabletAlias + Cnf *mysqlctl.Mycnf MysqlDaemon mysqlctl.MysqlDaemon DBConfigs *dbconfigs.DBConfigs BinlogPlayerMap *BinlogPlayerMap @@ -228,12 +229,18 @@ func NewActionAgent( batchCtx: batchCtx, TopoServer: ts, TabletAlias: tabletAlias, + Cnf: mycnf, MysqlDaemon: mysqld, DBConfigs: dbcfgs, History: history.New(historyLength), _healthy: fmt.Errorf("healthcheck not run yet"), orc: orc, } + // Sanity check for inconsistent flags + if agent.Cnf == nil && *restoreFromBackup { + return nil, fmt.Errorf("you cannot enable -restore_from_backup without a my.cnf file") + } + agent.registerQueryRuleSources() // try to initialize the tablet if we have to @@ -260,16 +267,11 @@ func NewActionAgent( mysqlHost = appConfig.Host mysqlPort = int32(appConfig.Port) } else { - // Assume unix socket was specified and try to figure out the mysql port - // by other means. - mysqlPort = mycnf.MysqlPort - if mysqlPort == 0 { - // we don't know the port, try to get it from mysqld - var err error - mysqlPort, err = mysqld.GetMysqlPort() - if err != nil { - log.Warningf("Cannot get current mysql port, will use 0 for now: %v", err) - } + // Assume unix socket was specified and try to get the port from mysqld + var err error + mysqlPort, err = mysqld.GetMysqlPort() + if err != nil { + log.Warningf("Cannot get current mysql port, will try to get it later: %v", err) } } @@ -336,6 +338,7 @@ func NewTestActionAgent(batchCtx context.Context, ts *topo.Server, tabletAlias * batchCtx: batchCtx, TopoServer: ts, TabletAlias: tabletAlias, + Cnf: nil, MysqlDaemon: mysqlDaemon, DBConfigs: &dbconfigs.DBConfigs{}, BinlogPlayerMap: nil, @@ -374,6 +377,7 @@ func NewComboActionAgent(batchCtx context.Context, ts *topo.Server, tabletAlias batchCtx: batchCtx, TopoServer: ts, TabletAlias: tabletAlias, + Cnf: nil, MysqlDaemon: mysqlDaemon, DBConfigs: dbcfgs, BinlogPlayerMap: nil, @@ -477,9 +481,14 @@ func (agent *ActionAgent) slaveStopped() bool { return *agent._slaveStopped } + // If there's no Cnf file, don't read state. + if agent.Cnf == nil { + return false + } + // If the marker file exists, we're stopped. // Treat any read error as if the file doesn't exist. - _, err := os.Stat(path.Join(agent.MysqlDaemon.TabletDir(), slaveStoppedFile)) + _, err := os.Stat(path.Join(agent.Cnf.TabletDir(), slaveStoppedFile)) slaveStopped := err == nil agent._slaveStopped = &slaveStopped return slaveStopped @@ -495,7 +504,10 @@ func (agent *ActionAgent) setSlaveStopped(slaveStopped bool) { // We store a marker in the filesystem so it works regardless of whether // mysqld is running, and so it's tied to this particular instance of the // tablet data dir (the one that's paused at a known replication position). - tabletDir := agent.MysqlDaemon.TabletDir() + if agent.Cnf == nil { + return + } + tabletDir := agent.Cnf.TabletDir() if tabletDir == "" { return } diff --git a/go/vt/vttablet/tabletmanager/restore.go b/go/vt/vttablet/tabletmanager/restore.go index 7c252e54d1c..030b575b4a2 100644 --- a/go/vt/vttablet/tabletmanager/restore.go +++ b/go/vt/vttablet/tabletmanager/restore.go @@ -48,6 +48,9 @@ func (agent *ActionAgent) RestoreData(ctx context.Context, logger logutil.Logger return err } defer agent.unlock() + if agent.Cnf == nil { + return fmt.Errorf("cannot perform restore without my.cnf, please restart vttablet with a my.cnf file specified") + } return agent.restoreDataLocked(ctx, logger, deleteBeforeRestore) } @@ -75,7 +78,7 @@ func (agent *ActionAgent) restoreDataLocked(ctx context.Context, logger logutil. localMetadata := agent.getLocalMetadataValues(originalType) tablet := agent.Tablet() dir := fmt.Sprintf("%v/%v", tablet.Keyspace, tablet.Shard) - pos, err := mysqlctl.Restore(ctx, agent.MysqlDaemon, dir, *restoreConcurrency, agent.hookExtraEnv(), localMetadata, logger, deleteBeforeRestore, topoproto.TabletDbName(tablet)) + pos, err := mysqlctl.Restore(ctx, agent.Cnf, agent.MysqlDaemon, dir, *restoreConcurrency, agent.hookExtraEnv(), localMetadata, logger, deleteBeforeRestore, topoproto.TabletDbName(tablet)) switch err { case nil: // Starting from here we won't be able to recover if we get stopped by a cancelled diff --git a/go/vt/vttablet/tabletmanager/rpc_backup.go b/go/vt/vttablet/tabletmanager/rpc_backup.go index 66d40b1a819..2ad3cc2d988 100644 --- a/go/vt/vttablet/tabletmanager/rpc_backup.go +++ b/go/vt/vttablet/tabletmanager/rpc_backup.go @@ -36,6 +36,10 @@ func (agent *ActionAgent) Backup(ctx context.Context, concurrency int, logger lo } defer agent.unlock() + if agent.Cnf == nil { + return fmt.Errorf("cannot perform backup without my.cnf, please restart vttablet with a my.cnf file specified") + } + // update our type to BACKUP tablet, err := agent.TopoServer.GetTablet(ctx, agent.TabletAlias) if err != nil { @@ -60,7 +64,7 @@ func (agent *ActionAgent) Backup(ctx context.Context, concurrency int, logger lo // now we can run the backup dir := fmt.Sprintf("%v/%v", tablet.Keyspace, tablet.Shard) name := fmt.Sprintf("%v.%v", time.Now().UTC().Format("2006-01-02.150405"), topoproto.TabletAliasString(tablet.Alias)) - returnErr := mysqlctl.Backup(ctx, agent.MysqlDaemon, l, dir, name, concurrency, agent.hookExtraEnv()) + returnErr := mysqlctl.Backup(ctx, agent.Cnf, agent.MysqlDaemon, l, dir, name, concurrency, agent.hookExtraEnv()) // change our type back to the original value _, err = topotools.ChangeType(ctx, agent.TopoServer, tablet.Alias, originalType) diff --git a/go/vt/wrangler/testlib/backup_test.go b/go/vt/wrangler/testlib/backup_test.go index 9c4ee85c552..e359fba6916 100644 --- a/go/vt/wrangler/testlib/backup_test.go +++ b/go/vt/wrangler/testlib/backup_test.go @@ -108,13 +108,14 @@ func TestBackupRestore(t *testing.T) { "STOP SLAVE", "START SLAVE", } - sourceTablet.FakeMysqlDaemon.Mycnf = &mysqlctl.Mycnf{ + sourceTablet.StartActionLoop(t, wr) + defer sourceTablet.StopActionLoop(t) + + sourceTablet.Agent.Cnf = &mysqlctl.Mycnf{ DataDir: sourceDataDir, InnodbDataHomeDir: sourceInnodbDataDir, InnodbLogGroupHomeDir: sourceInnodbLogDir, } - sourceTablet.StartActionLoop(t, wr) - defer sourceTablet.StopActionLoop(t) // run the backup if err := vp.Run([]string{"Backup", topoproto.TabletAliasString(sourceTablet.Tablet.Alias)}); err != nil { @@ -150,15 +151,6 @@ func TestBackupRestore(t *testing.T) { "FAKE SET MASTER", "START SLAVE", } - destTablet.FakeMysqlDaemon.Mycnf = &mysqlctl.Mycnf{ - DataDir: sourceDataDir, - InnodbDataHomeDir: sourceInnodbDataDir, - InnodbLogGroupHomeDir: sourceInnodbLogDir, - BinLogPath: path.Join(root, "bin-logs/filename_prefix"), - RelayLogPath: path.Join(root, "relay-logs/filename_prefix"), - RelayLogIndexPath: path.Join(root, "relay-log.index"), - RelayLogInfoPath: path.Join(root, "relay-log.info"), - } destTablet.FakeMysqlDaemon.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SHOW DATABASES": {}, } @@ -168,6 +160,16 @@ func TestBackupRestore(t *testing.T) { destTablet.StartActionLoop(t, wr) defer destTablet.StopActionLoop(t) + destTablet.Agent.Cnf = &mysqlctl.Mycnf{ + DataDir: sourceDataDir, + InnodbDataHomeDir: sourceInnodbDataDir, + InnodbLogGroupHomeDir: sourceInnodbLogDir, + BinLogPath: path.Join(root, "bin-logs/filename_prefix"), + RelayLogPath: path.Join(root, "relay-logs/filename_prefix"), + RelayLogIndexPath: path.Join(root, "relay-log.index"), + RelayLogInfoPath: path.Join(root, "relay-log.info"), + } + if err := destTablet.Agent.RestoreData(ctx, logutil.NewConsoleLogger(), false /* deleteBeforeRestore */); err != nil { t.Fatalf("RestoreData failed: %v", err) } diff --git a/test/tabletmanager.py b/test/tabletmanager.py index 1ce2c27b571..2cfc236aabc 100755 --- a/test/tabletmanager.py +++ b/test/tabletmanager.py @@ -191,6 +191,15 @@ def test_command_line(self): qr = tablet_62044.execute('select id, msg from vt_select_test') self.assertEqual(len(qr['rows']), 4, 'expected 4 rows in vt_select_test: %s' % str(qr)) + + # Verify backup fails + try: + utils.run_vtctl(['Backup', tablet_62044.tablet_alias]) + except Exception as e: + self.assertIn('cannot perform backup without my.cnf', str(e)) + else: + self.assertFail('did not get an exception') + tablet_62044.kill_vttablet() def test_actions_and_timeouts(self):