feat(maglevc): JSON output for all commands (-json)
Enable App.JSON and add -json on golang-cli v1.4.0, mirroring evpnc so
the two CLIs' JSON contract is identical: show/query -> data, set/action
-> {}, failure -> {"error": "..."}.
- show/query commands branch on cli.IsJSON() -> emit the protobuf via
cli.EmitJSON; text keeps the tabwriter painters (which robot tests
parse via show, not via setter output).
- action commands (pause/resume/enable/disable, set weight, sync,
config reload) are now silent on success in text too — "we did what
you asked" needs no confirmation — and print "{}" in JSON via wrapJSON.
- config check stays informative (it is a query): text "config ok" /
error, JSON the CheckConfig report.
- errors: formatError returns {"error": "..."} in JSON mode.
- watch streams its own JSON events (no trailing {}).
Robot tests assert backend state via `show`, not setter stdout, so the
dropped confirmations don't affect them. Builds on linux and openbsd.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+52
-56
@@ -230,6 +230,9 @@ func buildTree() *node {
|
||||
}
|
||||
|
||||
root.Children = []*node{show, set, watch, configNode, syncNode, quit, exit}
|
||||
// In -json mode, make every silent successful command print "{}" (see
|
||||
// wrapJSON); show/return-data commands emit their own JSON first.
|
||||
wrapJSON(root)
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -272,6 +275,9 @@ func runShowVPPInfo(ctx context.Context, client grpcapi.MaglevClient, _ []string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(info)
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("version"), info.Version)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("build-date"), info.BuildDate)
|
||||
@@ -297,6 +303,9 @@ func runShowVPPLBState(ctx context.Context, client grpcapi.MaglevClient, _ []str
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(state)
|
||||
}
|
||||
|
||||
// ---- global config ----
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
@@ -354,6 +363,9 @@ func runShowVPPLBCounters(ctx context.Context, client grpcapi.MaglevClient, _ []
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(resp)
|
||||
}
|
||||
|
||||
if len(resp.Vips) == 0 {
|
||||
fmt.Println("(no counters — VPP disconnected or scrape pending)")
|
||||
@@ -417,18 +429,18 @@ func runSyncVPPLBState(ctx context.Context, client grpcapi.MaglevClient, args []
|
||||
name := args[0]
|
||||
req.FrontendName = &name
|
||||
}
|
||||
if _, err := client.SyncVPPLBState(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.FrontendName != nil {
|
||||
fmt.Printf("synced frontend %q to VPP\n", *req.FrontendName)
|
||||
} else {
|
||||
fmt.Println("synced full LB state to VPP")
|
||||
}
|
||||
return nil
|
||||
_, err := client.SyncVPPLBState(ctx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func runShowVersion(_ context.Context, _ grpcapi.MaglevClient, _ []string) error {
|
||||
if cli.IsJSON() {
|
||||
return emitJSON(map[string]string{
|
||||
"version": buildinfo.Version(),
|
||||
"commit": buildinfo.Commit(),
|
||||
"built": buildinfo.Date(),
|
||||
})
|
||||
}
|
||||
fmt.Printf("maglevc %s (commit %s, built %s)\n",
|
||||
buildinfo.Version(), buildinfo.Commit(), buildinfo.Date())
|
||||
return nil
|
||||
@@ -445,6 +457,9 @@ func runShowFrontends(ctx context.Context, client grpcapi.MaglevClient, _ []stri
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(resp)
|
||||
}
|
||||
for _, name := range resp.FrontendNames {
|
||||
fmt.Println(name)
|
||||
}
|
||||
@@ -461,6 +476,9 @@ func runShowFrontend(ctx context.Context, client grpcapi.MaglevClient, args []st
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(info)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("name"), info.Name)
|
||||
@@ -522,6 +540,9 @@ func runShowBackends(ctx context.Context, client grpcapi.MaglevClient, _ []strin
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(resp)
|
||||
}
|
||||
for _, name := range resp.BackendNames {
|
||||
fmt.Println(name)
|
||||
}
|
||||
@@ -538,6 +559,9 @@ func runShowBackend(ctx context.Context, client grpcapi.MaglevClient, args []str
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(info)
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("name"), info.Name)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("address"), info.Address)
|
||||
@@ -577,6 +601,9 @@ func runShowHealthChecks(ctx context.Context, client grpcapi.MaglevClient, _ []s
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(resp)
|
||||
}
|
||||
for _, name := range resp.Names {
|
||||
fmt.Println(name)
|
||||
}
|
||||
@@ -593,6 +620,9 @@ func runShowHealthCheck(ctx context.Context, client grpcapi.MaglevClient, args [
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(info)
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("name"), info.Name)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("type"), info.Type)
|
||||
@@ -640,12 +670,8 @@ func runPauseBackend(ctx context.Context, client grpcapi.MaglevClient, args []st
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||
defer cancel()
|
||||
info, err := client.PauseBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s: setting state to '%s'\n", info.Name, info.State)
|
||||
return nil
|
||||
_, err := client.PauseBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
return err
|
||||
}
|
||||
|
||||
func runResumeBackend(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
||||
@@ -654,12 +680,8 @@ func runResumeBackend(ctx context.Context, client grpcapi.MaglevClient, args []s
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||
defer cancel()
|
||||
info, err := client.ResumeBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s: setting state to '%s'\n", info.Name, info.State)
|
||||
return nil
|
||||
_, err := client.ResumeBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
return err
|
||||
}
|
||||
|
||||
func runSetFrontendPoolBackendWeight(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
||||
@@ -681,34 +703,14 @@ func setFrontendPoolBackendWeight(ctx context.Context, client grpcapi.MaglevClie
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||
defer cancel()
|
||||
info, err := client.SetFrontendPoolBackendWeight(ctx, &grpcapi.SetWeightRequest{
|
||||
_, err = client.SetFrontendPoolBackendWeight(ctx, &grpcapi.SetWeightRequest{
|
||||
Frontend: frontendName,
|
||||
Pool: poolName,
|
||||
Backend: backendName,
|
||||
Weight: int32(weight),
|
||||
Flush: flush,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Print the updated pool so the user can confirm the new weight.
|
||||
for _, pool := range info.Pools {
|
||||
if pool.Name != poolName {
|
||||
continue
|
||||
}
|
||||
for _, pb := range pool.Backends {
|
||||
if pb.Name == backendName {
|
||||
flushNote := ""
|
||||
if flush {
|
||||
flushNote = " (flushed)"
|
||||
}
|
||||
fmt.Printf("%s pool %s backend %s: weight set to %d%s\n",
|
||||
info.Name, pool.Name, pb.Name, pb.Weight, flushNote)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func runEnableBackend(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
||||
@@ -717,12 +719,8 @@ func runEnableBackend(ctx context.Context, client grpcapi.MaglevClient, args []s
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||
defer cancel()
|
||||
info, err := client.EnableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s: enabled, state is '%s'\n", info.Name, info.State)
|
||||
return nil
|
||||
_, err := client.EnableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
return err
|
||||
}
|
||||
|
||||
func runDisableBackend(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
||||
@@ -731,12 +729,8 @@ func runDisableBackend(ctx context.Context, client grpcapi.MaglevClient, args []
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||
defer cancel()
|
||||
info, err := client.DisableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s: disabled, state is '%s'\n", info.Name, info.State)
|
||||
return nil
|
||||
_, err := client.DisableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||
return err
|
||||
}
|
||||
|
||||
func runConfigCheck(ctx context.Context, client grpcapi.MaglevClient, _ []string) error {
|
||||
@@ -746,6 +740,9 @@ func runConfigCheck(ctx context.Context, client grpcapi.MaglevClient, _ []string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cli.IsJSON() {
|
||||
return emitProto(resp)
|
||||
}
|
||||
if resp.Ok {
|
||||
fmt.Println("config ok")
|
||||
return nil
|
||||
@@ -764,7 +761,6 @@ func runConfigReload(ctx context.Context, client grpcapi.MaglevClient, _ []strin
|
||||
return err
|
||||
}
|
||||
if resp.Ok {
|
||||
fmt.Println("config reloaded")
|
||||
return nil
|
||||
}
|
||||
if resp.ParseError != "" {
|
||||
|
||||
Reference in New Issue
Block a user