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 }