Skip to content

Commit

Permalink
Merge pull request #8003 from tinyspeck/am_vtadmin_experimental_table…
Browse files Browse the repository at this point in the history
…tdebug

[vtadmin] experimental tabletdebug
  • Loading branch information
ajm188 authored Apr 30, 2021
2 parents 704f98f + 811b8e4 commit 251d632
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 4 deletions.
7 changes: 7 additions & 0 deletions go/cmd/vtadmin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ func main() {
rootCmd.Flags().BoolVar(&httpOpts.EnableTracing, "http-tracing", false, "whether to enable tracing on the HTTP server")
rootCmd.Flags().BoolVar(&httpOpts.DisableCompression, "http-no-compress", false, "whether to disable compression of HTTP API responses")
rootCmd.Flags().StringSliceVar(&httpOpts.CORSOrigins, "http-origin", []string{}, "repeated, comma-separated flag of allowed CORS origins. omit to disable CORS")
rootCmd.Flags().StringVar(&httpOpts.ExperimentalOptions.TabletFQDNTmpl,
"http-tablet-fqdn-tmpl",
"{{ .Tablet.Hostname }}:80",
"[EXPERIMENTAL] Go template string to generate a reachable http "+
"address for a tablet. Currently used to make passthrough "+
"requests to /debug/vars endpoints.",
)

// glog flags, no better way to do this
rootCmd.Flags().AddGoFlag(flag.Lookup("v"))
Expand Down
6 changes: 5 additions & 1 deletion go/vt/vtadmin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"vitess.io/vitess/go/vt/vtadmin/errors"
"vitess.io/vitess/go/vt/vtadmin/grpcserver"
vtadminhttp "vitess.io/vitess/go/vt/vtadmin/http"
"vitess.io/vitess/go/vt/vtadmin/http/experimental"
vthandlers "vitess.io/vitess/go/vt/vtadmin/http/handlers"
"vitess.io/vitess/go/vt/vtadmin/sort"
"vitess.io/vitess/go/vt/vtadmin/vtadminproto"
Expand Down Expand Up @@ -100,7 +101,7 @@ func NewAPI(clusters []*cluster.Cluster, opts grpcserver.Options, httpOpts vtadm

vtadminpb.RegisterVTAdminServer(serv.GRPCServer(), api)

httpAPI := vtadminhttp.NewAPI(api)
httpAPI := vtadminhttp.NewAPI(api, httpOpts)

router.HandleFunc("/clusters", httpAPI.Adapt(vtadminhttp.GetClusters)).Name("API.GetClusters")
router.HandleFunc("/gates", httpAPI.Adapt(vtadminhttp.GetGates)).Name("API.GetGates")
Expand All @@ -116,6 +117,9 @@ func NewAPI(clusters []*cluster.Cluster, opts grpcserver.Options, httpOpts vtadm
router.HandleFunc("/workflow/{cluster_id}/{keyspace}/{name}", httpAPI.Adapt(vtadminhttp.GetWorkflow)).Name("API.GetWorkflow")
router.HandleFunc("/workflows", httpAPI.Adapt(vtadminhttp.GetWorkflows)).Name("API.GetWorkflows")

experimentalRouter := router.PathPrefix("/experimental").Subrouter()
experimentalRouter.HandleFunc("/tablet/{tablet}/debug/vars", httpAPI.Adapt(experimental.TabletDebugVarsPassthrough)).Name("API.TabletDebugVarsPassthrough")

// Middlewares are executed in order of addition. Our ordering (all
// middlewares being optional) is:
// 1. CORS. CORS is a special case and is applied globally, the rest are applied only to the subrouter.
Expand Down
20 changes: 17 additions & 3 deletions go/vt/vtadmin/http/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ type Options struct {
// DisableCompression specifies whether to turn off gzip compression for API
// endpoints. It is named as the negative (as opposed to EnableTracing) so
// the zero value has compression enabled.
DisableCompression bool
DisableCompression bool
ExperimentalOptions struct {
TabletFQDNTmpl string
}
}

// API is used to power HTTP endpoint wrappers to the VTAdminServer interface.
type API struct {
server vtadminpb.VTAdminServer
opts Options
}

// NewAPI returns an HTTP API backed by the given VTAdminServer implementation.
func NewAPI(server vtadminpb.VTAdminServer) *API {
return &API{server: server}
func NewAPI(server vtadminpb.VTAdminServer, opts Options) *API {
return &API{server: server, opts: opts}
}

// VTAdminHandler is an HTTP endpoint handler that takes, via injection,
Expand All @@ -69,3 +73,13 @@ func (api *API) Adapt(handler VTAdminHandler) http.HandlerFunc {
handler(ctx, Request{r}, api).Write(w)
}
}

// Options returns a copy of the Options this API was configured with.
func (api *API) Options() Options {
return api.opts
}

// Server returns the VTAdminServer wrapped by this API.
func (api *API) Server() vtadminpb.VTAdminServer {
return api.server
}
87 changes: 87 additions & 0 deletions go/vt/vtadmin/http/experimental/tablets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
Copyright 2021 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package experimental

import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
"text/template"

vtadminhttp "vitess.io/vitess/go/vt/vtadmin/http"

vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
)

// TabletDebugVarsPassthrough makes a passthrough request to a tablet's
// /debug/vars route, after looking up the tablet via VTAdmin's GetTablet
// rpc.
func TabletDebugVarsPassthrough(ctx context.Context, r vtadminhttp.Request, api *vtadminhttp.API) *vtadminhttp.JSONResponse {
vars := r.Vars()

tablet, err := api.Server().GetTablet(ctx, &vtadminpb.GetTabletRequest{
Hostname: vars["tablet"],
ClusterIds: r.URL.Query()["cluster"],
})

if err != nil {
return vtadminhttp.NewJSONResponse(nil, err)
}

debugVars, err := getDebugVars(ctx, api, tablet)
return vtadminhttp.NewJSONResponse(debugVars, err)
}

func getDebugVars(ctx context.Context, api *vtadminhttp.API, tablet *vtadminpb.Tablet) (map[string]interface{}, error) {
tmpl, err := template.New("tablet-fqdn").Parse(api.Options().ExperimentalOptions.TabletFQDNTmpl)
if err != nil {
return nil, err
}

buf := bytes.NewBuffer(nil)
if err := tmpl.Execute(buf, tablet); err != nil {
return nil, err
}
_, _ = buf.WriteString("/debug/vars")

url := buf.String()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}

defer resp.Body.Close()

data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

var debugVars map[string]interface{}
if err := json.Unmarshal(data, &debugVars); err != nil {
return nil, err
}

return debugVars, nil
}

0 comments on commit 251d632

Please sign in to comment.