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

     1  /*
     2  Copyright 2020 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  	"flag"
    22  	"io"
    23  	"time"
    24  
    25  	"github.com/spf13/cobra"
    26  
    27  	"vitess.io/vitess/go/trace"
    28  	"vitess.io/vitess/go/vt/log"
    29  	"vitess.io/vitess/go/vt/logutil"
    30  	"vitess.io/vitess/go/vt/servenv"
    31  	"vitess.io/vitess/go/vt/vtadmin"
    32  	"vitess.io/vitess/go/vt/vtadmin/cache"
    33  	"vitess.io/vitess/go/vt/vtadmin/cluster"
    34  	"vitess.io/vitess/go/vt/vtadmin/grpcserver"
    35  	vtadminhttp "vitess.io/vitess/go/vt/vtadmin/http"
    36  	"vitess.io/vitess/go/vt/vtadmin/http/debug"
    37  	"vitess.io/vitess/go/vt/vtadmin/rbac"
    38  
    39  	_flag "vitess.io/vitess/go/internal/flag"
    40  )
    41  
    42  var (
    43  	opts                  grpcserver.Options
    44  	httpOpts              vtadminhttp.Options
    45  	clusterConfigs        cluster.ClustersFlag
    46  	clusterFileConfig     cluster.FileConfig
    47  	defaultClusterConfig  cluster.Config
    48  	enableDynamicClusters bool
    49  
    50  	rbacConfigPath string
    51  	enableRBAC     bool
    52  	disableRBAC    bool
    53  
    54  	cacheRefreshKey string
    55  
    56  	traceCloser io.Closer = &noopCloser{}
    57  
    58  	rootCmd = &cobra.Command{
    59  		Use: "vtadmin",
    60  		PreRun: func(cmd *cobra.Command, args []string) {
    61  			_flag.TrickGlog()
    62  			logutil.PurgeLogs()
    63  
    64  			if opts.EnableTracing || httpOpts.EnableTracing {
    65  				startTracing(cmd)
    66  			}
    67  		},
    68  		Run: run,
    69  		PostRun: func(cmd *cobra.Command, args []string) {
    70  			trace.LogErrorsWhenClosing(traceCloser)
    71  		},
    72  		Version: servenv.AppVersion.String(),
    73  	}
    74  )
    75  
    76  // fatal ensures the tracer is closed and final spans are sent before issuing
    77  // a log.Fatal call with the given args.
    78  func fatal(args ...any) {
    79  	trace.LogErrorsWhenClosing(traceCloser)
    80  	log.Fatal(args...)
    81  }
    82  
    83  // startTracing checks the value of --tracer and then starts tracing, populating
    84  // the private global traceCloser
    85  func startTracing(cmd *cobra.Command) {
    86  	tracer, err := cmd.Flags().GetString("tracer")
    87  	if err != nil {
    88  		log.Warningf("not starting tracer; err: %s", err)
    89  		return
    90  	}
    91  
    92  	if tracer == "" || tracer == "noop" {
    93  		log.Warningf("starting tracing with noop tracer")
    94  	}
    95  
    96  	traceCloser = trace.StartTracing("vtadmin")
    97  }
    98  
    99  func run(cmd *cobra.Command, args []string) {
   100  	bootSpan, ctx := trace.NewSpan(context.Background(), "vtadmin.boot")
   101  	defer bootSpan.Finish()
   102  
   103  	configs := clusterFileConfig.Combine(defaultClusterConfig, clusterConfigs)
   104  	clusters := make([]*cluster.Cluster, len(configs))
   105  
   106  	if len(configs) == 0 && !enableDynamicClusters {
   107  		bootSpan.Finish()
   108  		fatal("must specify at least one cluster")
   109  	}
   110  
   111  	var rbacConfig *rbac.Config
   112  	if disableRBAC {
   113  		rbacConfig = rbac.DefaultConfig()
   114  	} else if enableRBAC && rbacConfigPath != "" {
   115  		cfg, err := rbac.LoadConfig(rbacConfigPath)
   116  		if err != nil {
   117  			fatal(err)
   118  		}
   119  
   120  		rbacConfig = cfg
   121  	} else if enableRBAC && rbacConfigPath == "" {
   122  		fatal("must pass --rbac-config path when enabling rbac")
   123  	} else {
   124  		fatal("must explicitly enable or disable RBAC by passing --no-rbac or --rbac")
   125  	}
   126  
   127  	for i, cfg := range configs {
   128  		cluster, err := cfg.Cluster(ctx)
   129  		if err != nil {
   130  			bootSpan.Finish()
   131  			fatal(err)
   132  		}
   133  
   134  		clusters[i] = cluster
   135  	}
   136  
   137  	if cacheRefreshKey == "" {
   138  		log.Warningf("no cache-refresh-key set; forcing cache refreshes will not be possible")
   139  	}
   140  	cache.SetCacheRefreshKey(cacheRefreshKey)
   141  
   142  	s := vtadmin.NewAPI(clusters, vtadmin.Options{
   143  		GRPCOpts:              opts,
   144  		HTTPOpts:              httpOpts,
   145  		RBAC:                  rbacConfig,
   146  		EnableDynamicClusters: enableDynamicClusters,
   147  	})
   148  	bootSpan.Finish()
   149  
   150  	if err := s.ListenAndServe(); err != nil {
   151  		fatal(err)
   152  	}
   153  }
   154  
   155  func main() {
   156  	// Common flags
   157  	rootCmd.Flags().StringVar(&opts.Addr, "addr", ":15000", "address to serve on")
   158  	rootCmd.Flags().DurationVar(&opts.CMuxReadTimeout, "lmux-read-timeout", time.Second, "how long to spend connection muxing")
   159  	rootCmd.Flags().DurationVar(&opts.LameDuckDuration, "lame-duck-duration", time.Second*5, "length of lame duck period at shutdown")
   160  
   161  	// Cluster config flags
   162  	rootCmd.Flags().Var(&clusterConfigs, "cluster", "per-cluster configuration. any values here take precedence over those in -cluster-defaults or -cluster-config")
   163  	rootCmd.Flags().Var(&clusterFileConfig, "cluster-config", "path to a yaml cluster configuration. see clusters.example.yaml") // (TODO:@amason) provide example config.
   164  	rootCmd.Flags().Var(&defaultClusterConfig, "cluster-defaults", "default options for all clusters")
   165  	rootCmd.Flags().BoolVar(&enableDynamicClusters, "enable-dynamic-clusters", false, "whether to enable dynamic clusters that are set by request header cookies or gRPC metadata")
   166  
   167  	// Tracing flags
   168  	trace.RegisterFlags(rootCmd.Flags()) // defined in go/vt/trace
   169  	rootCmd.Flags().BoolVar(&opts.EnableTracing, "grpc-tracing", false, "whether to enable tracing on the gRPC server")
   170  	rootCmd.Flags().BoolVar(&httpOpts.EnableTracing, "http-tracing", false, "whether to enable tracing on the HTTP server")
   171  
   172  	// gRPC server flags
   173  	rootCmd.Flags().BoolVar(&opts.AllowReflection, "grpc-allow-reflection", false, "whether to register the gRPC server for reflection; this is required to use tools like grpc_cli")
   174  	rootCmd.Flags().BoolVar(&opts.EnableChannelz, "grpc-enable-channelz", false, "whether to enable the channelz service on the gRPC server")
   175  
   176  	// HTTP server flags
   177  	rootCmd.Flags().BoolVar(&httpOpts.DisableCompression, "http-no-compress", false, "whether to disable compression of HTTP API responses")
   178  	rootCmd.Flags().BoolVar(&httpOpts.DisableDebug, "http-no-debug", false, "whether to disable /debug/pprof/* and /debug/env HTTP endpoints")
   179  	rootCmd.Flags().Var(&debug.OmitEnv, "http-debug-omit-env", "name of an environment variable to omit from /debug/env, if http debug endpoints are enabled. specify multiple times to omit multiple env vars")
   180  	rootCmd.Flags().Var(&debug.SanitizeEnv, "http-debug-sanitize-env", "name of an environment variable to sanitize in /debug/env, if http debug endpoints are enabled. specify multiple times to sanitize multiple env vars")
   181  	rootCmd.Flags().StringVar(&opts.MetricsEndpoint, "http-metrics-endpoint", "/metrics",
   182  		"HTTP endpoint to expose prometheus metrics on. Omit to disable scraping metrics. "+
   183  			"Using a path used by VTAdmin's http API is unsupported and causes undefined behavior.")
   184  	rootCmd.Flags().StringSliceVar(&httpOpts.CORSOrigins, "http-origin", []string{}, "repeated, comma-separated flag of allowed CORS origins. omit to disable CORS")
   185  	rootCmd.Flags().StringVar(&httpOpts.ExperimentalOptions.TabletURLTmpl,
   186  		"http-tablet-url-tmpl",
   187  		"https://{{ .Tablet.Hostname }}:80",
   188  		"[EXPERIMENTAL] Go template string to generate a reachable http(s) "+
   189  			"address for a tablet. Currently used to make passthrough "+
   190  			"requests to /debug/vars endpoints.",
   191  	)
   192  
   193  	// RBAC flags
   194  	rootCmd.Flags().StringVar(&rbacConfigPath, "rbac-config", "", "path to an RBAC config file. must be set if passing --rbac")
   195  	rootCmd.Flags().BoolVar(&enableRBAC, "rbac", false, "whether to enable RBAC. must be set if not passing --rbac")
   196  	rootCmd.Flags().BoolVar(&disableRBAC, "no-rbac", false, "whether to disable RBAC. must be set if not passing --no-rbac")
   197  
   198  	// Global cache flags (N.B. there are also cluster-specific cache flags)
   199  	cacheRefreshHelp := "instructs a request to ignore any cached data (if applicable) and refresh the cache;" +
   200  		"usable as an HTTP header named 'X-<key>' and as a gRPC metadata key '<key>'\n" +
   201  		"Note: any whitespace characters are replaced with hyphens."
   202  	rootCmd.Flags().StringVar(&cacheRefreshKey, "cache-refresh-key", "vt-cache-refresh", cacheRefreshHelp)
   203  
   204  	// glog flags, no better way to do this
   205  	rootCmd.Flags().AddGoFlag(flag.Lookup("v"))
   206  	rootCmd.Flags().AddGoFlag(flag.Lookup("logtostderr"))
   207  	rootCmd.Flags().AddGoFlag(flag.Lookup("alsologtostderr"))
   208  	rootCmd.Flags().AddGoFlag(flag.Lookup("stderrthreshold"))
   209  	rootCmd.Flags().AddGoFlag(flag.Lookup("log_dir"))
   210  
   211  	if err := rootCmd.Execute(); err != nil {
   212  		log.Fatal(err)
   213  	}
   214  
   215  	log.Flush()
   216  }
   217  
   218  type noopCloser struct{}
   219  
   220  func (nc *noopCloser) Close() error { return nil }