vitess.io/vitess@v0.16.2/go/cmd/mysqlctl/mysqlctl.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // mysqlctl initializes and controls mysqld with Vitess-specific configuration.
    18  package main
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/spf13/pflag"
    27  
    28  	"vitess.io/vitess/go/acl"
    29  	"vitess.io/vitess/go/cmd"
    30  	"vitess.io/vitess/go/exit"
    31  	"vitess.io/vitess/go/flagutil"
    32  	"vitess.io/vitess/go/mysql"
    33  	"vitess.io/vitess/go/vt/dbconfigs"
    34  	"vitess.io/vitess/go/vt/log"
    35  	"vitess.io/vitess/go/vt/logutil"
    36  	"vitess.io/vitess/go/vt/mysqlctl"
    37  	"vitess.io/vitess/go/vt/servenv"
    38  )
    39  
    40  var (
    41  	mysqlPort   = 3306
    42  	tabletUID   = uint(41983)
    43  	mysqlSocket string
    44  )
    45  
    46  func init() {
    47  	servenv.RegisterDefaultSocketFileFlags()
    48  	servenv.RegisterFlags()
    49  	servenv.RegisterServiceMapFlag()
    50  	// mysqlctl only starts and stops mysql, only needs dba.
    51  	dbconfigs.RegisterFlags(dbconfigs.Dba)
    52  	servenv.OnParse(func(fs *pflag.FlagSet) {
    53  		fs.IntVar(&mysqlPort, "mysql_port", mysqlPort, "MySQL port")
    54  		fs.UintVar(&tabletUID, "tablet_uid", tabletUID, "Tablet UID")
    55  		fs.StringVar(&mysqlSocket, "mysql_socket", mysqlSocket, "Path to the mysqld socket file")
    56  
    57  		acl.RegisterFlags(fs)
    58  	})
    59  }
    60  
    61  func initConfigCmd(subFlags *pflag.FlagSet, args []string) error {
    62  	_ = subFlags.Parse(args)
    63  
    64  	// Generate my.cnf from scratch and use it to find mysqld.
    65  	mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(uint32(tabletUID), mysqlSocket, int32(mysqlPort))
    66  	if err != nil {
    67  		return fmt.Errorf("failed to initialize mysql config: %v", err)
    68  	}
    69  	defer mysqld.Close()
    70  	if err := mysqld.InitConfig(cnf); err != nil {
    71  		return fmt.Errorf("failed to init mysql config: %v", err)
    72  	}
    73  	return nil
    74  }
    75  
    76  func initCmd(subFlags *pflag.FlagSet, args []string) error {
    77  	waitTime := subFlags.Duration("wait_time", 5*time.Minute, "How long to wait for mysqld startup")
    78  	initDBSQLFile := subFlags.String("init_db_sql_file", "", "Path to .sql file to run after mysqld initiliaztion")
    79  	_ = subFlags.Parse(args)
    80  
    81  	// Generate my.cnf from scratch and use it to find mysqld.
    82  	mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(uint32(tabletUID), mysqlSocket, int32(mysqlPort))
    83  	if err != nil {
    84  		return fmt.Errorf("failed to initialize mysql config: %v", err)
    85  	}
    86  	defer mysqld.Close()
    87  
    88  	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
    89  	defer cancel()
    90  	if err := mysqld.Init(ctx, cnf, *initDBSQLFile); err != nil {
    91  		return fmt.Errorf("failed init mysql: %v", err)
    92  	}
    93  	return nil
    94  }
    95  
    96  func reinitConfigCmd(subFlags *pflag.FlagSet, args []string) error {
    97  	_ = subFlags.Parse(args)
    98  
    99  	// There ought to be an existing my.cnf, so use it to find mysqld.
   100  	mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(tabletUID))
   101  	if err != nil {
   102  		return fmt.Errorf("failed to find mysql config: %v", err)
   103  	}
   104  	defer mysqld.Close()
   105  
   106  	if err := mysqld.ReinitConfig(context.TODO(), cnf); err != nil {
   107  		return fmt.Errorf("failed to reinit mysql config: %v", err)
   108  	}
   109  	return nil
   110  }
   111  
   112  func shutdownCmd(subFlags *pflag.FlagSet, args []string) error {
   113  	waitTime := subFlags.Duration("wait_time", 5*time.Minute, "How long to wait for mysqld shutdown")
   114  	_ = subFlags.Parse(args)
   115  
   116  	// There ought to be an existing my.cnf, so use it to find mysqld.
   117  	mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(tabletUID))
   118  	if err != nil {
   119  		return fmt.Errorf("failed to find mysql config: %v", err)
   120  	}
   121  	defer mysqld.Close()
   122  
   123  	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
   124  	defer cancel()
   125  	if err := mysqld.Shutdown(ctx, cnf, true); err != nil {
   126  		return fmt.Errorf("failed shutdown mysql: %v", err)
   127  	}
   128  	return nil
   129  }
   130  
   131  func startCmd(subFlags *pflag.FlagSet, args []string) error {
   132  	waitTime := subFlags.Duration("wait_time", 5*time.Minute, "How long to wait for mysqld startup")
   133  	var mysqldArgs flagutil.StringListValue
   134  	subFlags.Var(&mysqldArgs, "mysqld_args", "List of comma-separated flags to pass additionally to mysqld")
   135  	_ = subFlags.Parse(args)
   136  
   137  	// There ought to be an existing my.cnf, so use it to find mysqld.
   138  	mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(tabletUID))
   139  	if err != nil {
   140  		return fmt.Errorf("failed to find mysql config: %v", err)
   141  	}
   142  	defer mysqld.Close()
   143  
   144  	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
   145  	defer cancel()
   146  	if err := mysqld.Start(ctx, cnf, mysqldArgs...); err != nil {
   147  		return fmt.Errorf("failed start mysql: %v", err)
   148  	}
   149  	return nil
   150  }
   151  
   152  func teardownCmd(subFlags *pflag.FlagSet, args []string) error {
   153  	waitTime := subFlags.Duration("wait_time", 5*time.Minute, "How long to wait for mysqld shutdown")
   154  	force := subFlags.Bool("force", false, "Remove the root directory even if mysqld shutdown fails")
   155  	_ = subFlags.Parse(args)
   156  
   157  	// There ought to be an existing my.cnf, so use it to find mysqld.
   158  	mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(uint32(tabletUID))
   159  	if err != nil {
   160  		return fmt.Errorf("failed to find mysql config: %v", err)
   161  	}
   162  	defer mysqld.Close()
   163  
   164  	ctx, cancel := context.WithTimeout(context.Background(), *waitTime)
   165  	defer cancel()
   166  	if err := mysqld.Teardown(ctx, cnf, *force); err != nil {
   167  		return fmt.Errorf("failed teardown mysql (forced? %v): %v", *force, err)
   168  	}
   169  	return nil
   170  }
   171  
   172  func positionCmd(subFlags *pflag.FlagSet, args []string) error {
   173  	_ = subFlags.Parse(args)
   174  	if len(args) < 3 {
   175  		return fmt.Errorf("not enough arguments for position operation")
   176  	}
   177  
   178  	pos1, err := mysql.DecodePosition(args[1])
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	switch args[0] {
   184  	case "equal":
   185  		pos2, err := mysql.DecodePosition(args[2])
   186  		if err != nil {
   187  			return err
   188  		}
   189  		fmt.Println(pos1.Equal(pos2))
   190  	case "at_least":
   191  		pos2, err := mysql.DecodePosition(args[2])
   192  		if err != nil {
   193  			return err
   194  		}
   195  		fmt.Println(pos1.AtLeast(pos2))
   196  	case "append":
   197  		gtid, err := mysql.DecodeGTID(args[2])
   198  		if err != nil {
   199  			return err
   200  		}
   201  		fmt.Println(mysql.AppendGTID(pos1, gtid))
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  type command struct {
   208  	name   string
   209  	method func(*pflag.FlagSet, []string) error
   210  	params string
   211  	help   string
   212  }
   213  
   214  var commands = []command{
   215  	{"init", initCmd, "[--wait_time=5m] [--init_db_sql_file=]",
   216  		"Initializes the directory structure and starts mysqld"},
   217  	{"init_config", initConfigCmd, "",
   218  		"Initializes the directory structure, creates my.cnf file, but does not start mysqld"},
   219  	{"reinit_config", reinitConfigCmd, "",
   220  		"Reinitializes my.cnf file with new server_id"},
   221  	{"teardown", teardownCmd, "[--wait_time=5m] [--force]",
   222  		"Shuts mysqld down, and removes the directory"},
   223  	{"start", startCmd, "[--wait_time=5m]",
   224  		"Starts mysqld on an already 'init'-ed directory"},
   225  	{"shutdown", shutdownCmd, "[--wait_time=5m]",
   226  		"Shuts down mysqld, does not remove any file"},
   227  
   228  	{"position", positionCmd,
   229  		"<operation> <pos1> <pos2 | gtid>",
   230  		"Compute operations on replication positions"},
   231  }
   232  
   233  func main() {
   234  	defer exit.Recover()
   235  	defer logutil.Flush()
   236  
   237  	fs := pflag.NewFlagSet("mysqlctl", pflag.ExitOnError)
   238  	log.RegisterFlags(fs)
   239  	logutil.RegisterFlags(fs)
   240  	pflag.Usage = func() {
   241  		w := os.Stderr
   242  		fmt.Fprintf(w, "Usage: %s [global-flags] <command> -- [command-flags]\n", os.Args[0])
   243  		fmt.Fprintf(w, "\nThe commands are listed below. Use '%s <command> -- {-h, --help}' for command help.\n\n", os.Args[0])
   244  		for _, cmd := range commands {
   245  			fmt.Fprintf(w, "  %s", cmd.name)
   246  			if cmd.params != "" {
   247  				fmt.Fprintf(w, " %s", cmd.params)
   248  			}
   249  			fmt.Fprintf(w, "\n")
   250  		}
   251  		fmt.Fprintf(w, "\nGlobal flags:\n")
   252  		pflag.PrintDefaults()
   253  	}
   254  	args := servenv.ParseFlagsWithArgs("mysqlctl")
   255  
   256  	if cmd.IsRunningAsRoot() {
   257  		fmt.Fprintln(os.Stderr, "mysqlctl cannot be ran as root. Please run as a different user")
   258  		exit.Return(1)
   259  	}
   260  
   261  	action := args[0]
   262  	for _, cmd := range commands {
   263  		if cmd.name == action {
   264  			subFlags := pflag.NewFlagSet(action, pflag.ExitOnError)
   265  			subFlags.Usage = func() {
   266  				w := os.Stderr
   267  				fmt.Fprintf(w, "Usage: %s %s %s\n\n", os.Args[0], cmd.name, cmd.params)
   268  				fmt.Fprintf(w, cmd.help)
   269  				fmt.Fprintf(w, "\n\n")
   270  				subFlags.PrintDefaults()
   271  			}
   272  			// This is logged and we want sentence capitalization and punctuation.
   273  			pflag.ErrHelp = fmt.Errorf("\nSee %s --help for more information.", os.Args[0]) // nolint:revive
   274  			if err := cmd.method(subFlags, args[1:]); err != nil {
   275  				log.Errorf("%v\n", err)
   276  				subFlags.Usage()
   277  				exit.Return(1)
   278  			}
   279  			return
   280  		}
   281  	}
   282  	log.Errorf("invalid action: %v\n\n", action)
   283  	pflag.Usage()
   284  	exit.Return(1)
   285  }