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 }