Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for json output in vtctl TopoCat command #4746

Merged
merged 2 commits into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go/cmd/zk/zkcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ func cmdCat(ctx context.Context, subFlags *flag.FlagSet, args []string) error {
}
decoded := ""
if *decodeProto {
decoded, err = vtctl.DecodeContent(zkPath, data)
decoded, err = vtctl.DecodeContent(zkPath, data, false)
if err != nil {
log.Warningf("cat: cannot proto decode %v: %v", zkPath, err)
decoded = string(data)
Expand Down
169 changes: 136 additions & 33 deletions go/vt/vtctl/topo.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ limitations under the License.
package vtctl

import (
"encoding/json"
"flag"
"fmt"
"github.com/golang/protobuf/jsonpb"
"io/ioutil"
"path"

Expand All @@ -42,7 +44,7 @@ func init() {
addCommand(topoGroupName, command{
"TopoCat",
commandTopoCat,
"[-cell <cell>] [-decode_proto] [-long] <path> [<path>...]",
"[-cell <cell>] [-decode_proto] [-decode_proto_json] [-long] <path> [<path>...]",
"Retrieves the file(s) at <path> from the topo service, and displays it. It can resolve wildcards, and decode the proto-encoded data."})

addCommand(topoGroupName, command{
Expand All @@ -54,7 +56,7 @@ func init() {

// DecodeContent uses the filename to imply a type, and proto-decodes
// the right object, then echoes it as a string.
func DecodeContent(filename string, data []byte) (string, error) {
func DecodeContent(filename string, data []byte, json bool) (string, error) {
name := path.Base(filename)

var p proto.Message
Expand All @@ -76,18 +78,28 @@ func DecodeContent(filename string, data []byte) (string, error) {
case topo.SrvKeyspaceFile:
p = new(topodatapb.SrvKeyspace)
default:
return string(data), nil
if json {
return "", fmt.Errorf("unknown topo protobuf type for %v", name)
} else {
return string(data), nil
}
}

if err := proto.Unmarshal(data, p); err != nil {
return string(data), err
}
return proto.MarshalTextString(p), nil

if json {
return new(jsonpb.Marshaler).MarshalToString(p)
} else {
return proto.MarshalTextString(p), nil
}
}

func commandTopoCat(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
cell := subFlags.String("cell", topo.GlobalCell, "topology cell to cat the file from. Defaults to global cell.")
long := subFlags.Bool("long", false, "long listing.")
decodeProtoJson := subFlags.Bool("decode_proto_json", false, "decode proto files and display them as json")
decodeProto := subFlags.Bool("decode_proto", false, "decode proto files and display them as text")
subFlags.Parse(args)
if subFlags.NArg() == 0 {
Expand All @@ -106,38 +118,17 @@ func commandTopoCat(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.F
if err != nil {
return err
}
hasError := false
for _, arg := range resolved {
data, version, err := conn.Get(ctx, arg)
if err != nil {
hasError = true
wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", arg, err)
continue
}

if *long {
wr.Logger().Printf("path=%v version=%v\n", arg, version)
}
decoded := ""
if *decodeProto {
decoded, err = DecodeContent(arg, data)
if err != nil {
wr.Logger().Warningf("TopoCat: cannot proto decode %v: %v", arg, err)
decoded = string(data)
}
} else {
decoded = string(data)
}
wr.Logger().Printf(decoded)
if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && *long {
wr.Logger().Printf("\n")
}
var topologyDecoder TopologyDecoder
if *decodeProtoJson {
topologyDecoder = JsonTopologyDecoder{}
} else if *decodeProto {
topologyDecoder = ProtoTopologyDecoder{}
} else {
topologyDecoder = PlainTopologyDecoder{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic nit.

Suggested change
topologyDecoder = PlainTopologyDecoder{}
switch {
case *decodeProtoJson:
topologyDecoder = JsonTopologyDecoder{}
case *decodeProto:
topologyDecoder = ProtoTopologyDecoder{}
default:
topologyDecoder = PlainTopologyDecoder{}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 updated!

}
if hasError {
return fmt.Errorf("TopoCat: some paths had errors")
}
return nil

return topologyDecoder.decode(resolved, conn, ctx, wr, *long)
}

func commandTopoCp(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
Expand Down Expand Up @@ -179,3 +170,115 @@ func copyFileToTopo(ctx context.Context, ts *topo.Server, cell, from, to string)
_, err = conn.Update(ctx, to, data, nil)
return err
}

type TopologyDecoder interface {
decode([]string, topo.Conn, context.Context, *wrangler.Wrangler, bool) error
}

type ProtoTopologyDecoder struct{}
type PlainTopologyDecoder struct{}
type JsonTopologyDecoder struct{}

func (d ProtoTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
hasError := false
for _, topoPath := range topoPaths {
data, version, err := conn.Get(ctx, topoPath)
if err != nil {
hasError = true
wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
continue
}

if long {
wr.Logger().Printf("path=%v version=%v\n", topoPath, version)
}

decoded, err := DecodeContent(topoPath, data, false)
if err != nil {
wr.Logger().Warningf("TopoCat: cannot proto decode %v: %v", topoPath, err)
decoded = string(data)
}

wr.Logger().Printf(decoded)
if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && long {
wr.Logger().Printf("\n")
}
}

if hasError {
return fmt.Errorf("TopoCat: some paths had errors")
}
return nil
}

func (d PlainTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
hasError := false
for _, topoPath := range topoPaths {
data, version, err := conn.Get(ctx, topoPath)
if err != nil {
hasError = true
wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
continue
}

if long {
wr.Logger().Printf("path=%v version=%v\n", topoPath, version)
}
decoded := string(data)
wr.Logger().Printf(decoded)
if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && long {
wr.Logger().Printf("\n")
}
}

if hasError {
return fmt.Errorf("TopoCat: some paths had errors")
}
return nil
}

func (d JsonTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
hasError := false
var jsonData []interface{}
for _, topoPath := range topoPaths {
data, version, err := conn.Get(ctx, topoPath)
if err != nil {
hasError = true
wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
continue
}

decoded, err := DecodeContent(topoPath, data, true)
if err != nil {
hasError = true
wr.Logger().Printf("TopoCat: cannot proto decode %v: %v", topoPath, err)
continue
}

var jsonDatum map[string]interface{}
if err = json.Unmarshal([]byte(decoded), &jsonDatum); err != nil {
hasError = true
wr.Logger().Printf("TopoCat: cannot json Unmarshal %v: %v", topoPath, err)
continue
}

if long {
jsonDatum["__path"] = topoPath
jsonDatum["__version"] = version.String()
}
jsonData = append(jsonData, jsonDatum)
}

jsonBytes, err := json.Marshal(jsonData)
if err != nil {
hasError = true
wr.Logger().Printf("TopoCat: cannot json Marshal: %v", err)
} else {
wr.Logger().Printf(string(jsonBytes) + "\n")
}

if hasError {
return fmt.Errorf("TopoCat: some paths had errors")
}
return nil
}
2 changes: 1 addition & 1 deletion go/vt/vtctld/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (ex *backendExplorer) HandlePath(nodePath string, r *http.Request) *Result
case nil:
if len(data) > 0 {
// It has contents, we just use it if possible.
decoded, err := vtctl.DecodeContent(relativePath, data)
decoded, err := vtctl.DecodeContent(relativePath, data, false)
if err != nil {
result.Error = err.Error()
} else {
Expand Down