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  }