vitess.io/vitess@v0.16.2/go/cmd/vtctl/vtctl.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 package main 18 19 import ( 20 "context" 21 "fmt" 22 "log/syslog" 23 "os" 24 "os/signal" 25 "strings" 26 "syscall" 27 "time" 28 29 "github.com/spf13/pflag" 30 31 "vitess.io/vitess/go/acl" 32 "vitess.io/vitess/go/cmd" 33 "vitess.io/vitess/go/cmd/vtctldclient/command" 34 "vitess.io/vitess/go/exit" 35 "vitess.io/vitess/go/trace" 36 "vitess.io/vitess/go/vt/log" 37 "vitess.io/vitess/go/vt/logutil" 38 "vitess.io/vitess/go/vt/servenv" 39 "vitess.io/vitess/go/vt/topo" 40 "vitess.io/vitess/go/vt/vtctl" 41 "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" 42 "vitess.io/vitess/go/vt/vtctl/localvtctldclient" 43 "vitess.io/vitess/go/vt/vttablet/tmclient" 44 "vitess.io/vitess/go/vt/wrangler" 45 ) 46 47 var ( 48 waitTime = 24 * time.Hour 49 detachedMode bool 50 ) 51 52 func init() { 53 servenv.OnParse(func(fs *pflag.FlagSet) { 54 // N.B. This is necessary for subcommand pflag parsing when not using 55 // cobra (cobra is where we're headed, but for `vtctl` it's a big lift 56 // before the RC cut). 57 // 58 // Essentially, the situation we have here is that commands look like: 59 // 60 // `vtctl [global flags] <command> [subcommand flags]` 61 // 62 // Since the default behavior of pflag is to allow "interspersed" flag 63 // and positional arguments, this means that the initial servenv parse 64 // will complain if _any_ subocmmand's flag is provided; for example, if 65 // you were to invoke 66 // 67 // `vtctl AddCellInfo --root /vitess/global --server_address "1.2.3.4" global 68 // 69 // then you would get the error "unknown flag --root", even though that 70 // is a valid flag for the AddCellInfo flag. 71 // 72 // By disabling interspersal on the top-level parse, anything after the 73 // command name ("AddCellInfo", in this example) will be forwarded to 74 // the subcommand's flag set for further parsing. 75 fs.SetInterspersed(false) 76 77 logger := logutil.NewConsoleLogger() 78 fs.SetOutput(logutil.NewLoggerWriter(logger)) 79 fs.Usage = func() { 80 logger.Printf("Usage: %s [global parameters] command [command parameters]\n", os.Args[0]) 81 logger.Printf("\nThe global optional parameters are:\n") 82 83 logger.Printf("%s\n", fs.FlagUsages()) 84 85 logger.Printf("\nThe commands are listed below, sorted by group. Use '%s <command> -h' for more help.\n\n", os.Args[0]) 86 vtctl.PrintAllCommands(logger) 87 } 88 89 fs.DurationVar(&waitTime, "wait-time", waitTime, "time to wait on an action") 90 fs.BoolVar(&detachedMode, "detach", detachedMode, "detached mode - run vtcl detached from the terminal") 91 92 acl.RegisterFlags(fs) 93 }) 94 } 95 96 // signal handling, centralized here 97 func installSignalHandlers(cancel func()) { 98 sigChan := make(chan os.Signal, 1) 99 signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) 100 go func() { 101 <-sigChan 102 // we got a signal, cancel the current ctx 103 cancel() 104 }() 105 } 106 107 func main() { 108 defer exit.RecoverAll() 109 defer logutil.Flush() 110 111 if detachedMode { 112 // this method will call os.Exit and kill this process 113 cmd.DetachFromTerminalAndExit() 114 } 115 116 args := servenv.ParseFlagsWithArgs("vtctl") 117 action := args[0] 118 119 startMsg := fmt.Sprintf("USER=%v SUDO_USER=%v %v", os.Getenv("USER"), os.Getenv("SUDO_USER"), strings.Join(os.Args, " ")) 120 121 if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil { 122 syslogger.Info(startMsg) // nolint:errcheck 123 } else { 124 log.Warningf("cannot connect to syslog: %v", err) 125 } 126 127 closer := trace.StartTracing("vtctl") 128 defer trace.LogErrorsWhenClosing(closer) 129 130 servenv.FireRunHooks() 131 132 ts := topo.Open() 133 defer ts.Close() 134 135 ctx, cancel := context.WithTimeout(context.Background(), waitTime) 136 installSignalHandlers(cancel) 137 138 // (TODO:ajm188) <Begin backwards compatibility support>. 139 // 140 // For v12, we are going to support new commands by prefixing as: 141 // vtctl VtctldCommand <command> <args...> 142 // 143 // Existing scripts will continue to use the legacy commands. This is the 144 // default case below. 145 // 146 // We will also support legacy commands by prefixing as: 147 // vtctl LegacyVtctlCommand <command> <args...> 148 // This is the fallthrough to the default case. 149 // 150 // In v13, we will make the default behavior to use the new commands and 151 // drop support for the `vtctl VtctldCommand ...` prefix, and legacy 152 // commands will only by runnable with the `vtctl LegacyVtctlCommand ...` 153 // prefix. 154 // 155 // In v14, we will drop support for all legacy commands, only running new 156 // commands, without any prefixing required or supported. 157 switch { 158 case strings.EqualFold(action, "VtctldCommand"): 159 // New behavior. Strip off the prefix, and set things up to run through 160 // the vtctldclient command tree, using the localvtctldclient (in-process) 161 // client. 162 vtctld := grpcvtctldserver.NewVtctldServer(ts) 163 localvtctldclient.SetServer(vtctld) 164 command.VtctldClientProtocol = "local" 165 166 os.Args = append([]string{"vtctldclient"}, args[1:]...) 167 if err := command.Root.ExecuteContext(ctx); err != nil { 168 log.Errorf("action failed: %v %v", action, err) 169 exit.Return(255) 170 } 171 case strings.EqualFold(action, "LegacyVtctlCommand"): 172 // Strip off the prefix (being used for compatibility) and fallthrough 173 // to the legacy behavior. 174 args = args[1:] 175 fallthrough 176 default: 177 log.Warningf("WARNING: vtctl should only be used for VDiff v1 workflows. Please use VDiff v2 and consider using vtctldclient for all other commands.") 178 179 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 180 181 if args[0] == "--" { 182 vtctl.PrintDoubleDashDeprecationNotice(wr) 183 args = args[1:] 184 } 185 186 action = args[0] 187 err := vtctl.RunCommand(ctx, wr, args) 188 cancel() 189 switch err { 190 case vtctl.ErrUnknownCommand: 191 pflag.Usage() 192 exit.Return(1) 193 case nil: 194 // keep going 195 default: 196 log.Errorf("action failed: %v %v", action, err) 197 exit.Return(255) 198 } 199 } 200 }