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:
+9
-4
@@ -3,6 +3,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
cli "git.ipng.ch/ipng/golang-cli"
|
cli "git.ipng.ch/ipng/golang-cli"
|
||||||
@@ -10,10 +11,10 @@ import (
|
|||||||
|
|
||||||
// formatError returns a user-friendly error string. gRPC status errors are
|
// formatError returns a user-friendly error string. gRPC status errors are
|
||||||
// unwrapped to show only the server's message (no "rpc error: code = ..."
|
// unwrapped to show only the server's message (no "rpc error: code = ..."
|
||||||
// boilerplate). The result is wrapped in red when color is enabled. The label()
|
// boilerplate). In JSON mode it returns the message as a {"error": "..."}
|
||||||
// helper and the ANSI palette now live in the golang-cli library (cli.Label,
|
// document (so failures are machine-readable, matching the data/`{}` a success
|
||||||
// cli.Red, ...); only this gRPC-specific unwrap stays here, wired in as
|
// prints); otherwise it wraps the message in red when color is enabled.
|
||||||
// App.FormatError.
|
// App.FormatError prints the result.
|
||||||
func formatError(err error) string {
|
func formatError(err error) string {
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
// google.golang.org/grpc/status errors format as:
|
// google.golang.org/grpc/status errors format as:
|
||||||
@@ -21,6 +22,10 @@ func formatError(err error) string {
|
|||||||
if i := strings.Index(msg, " desc = "); i >= 0 {
|
if i := strings.Index(msg, " desc = "); i >= 0 {
|
||||||
msg = msg[i+len(" desc = "):]
|
msg = msg[i+len(" desc = "):]
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
b, _ := json.Marshal(map[string]string{"error": msg})
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
if cli.ColorEnabled() {
|
if cli.ColorEnabled() {
|
||||||
return cli.Red + msg + cli.Reset
|
return cli.Red + msg + cli.Reset
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-56
@@ -230,6 +230,9 @@ func buildTree() *node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
root.Children = []*node{show, set, watch, configNode, syncNode, quit, exit}
|
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
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,6 +275,9 @@ func runShowVPPInfo(ctx context.Context, client grpcapi.MaglevClient, _ []string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(info)
|
||||||
|
}
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
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("version"), info.Version)
|
||||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("build-date"), info.BuildDate)
|
_, _ = 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(state)
|
||||||
|
}
|
||||||
|
|
||||||
// ---- global config ----
|
// ---- global config ----
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||||
@@ -354,6 +363,9 @@ func runShowVPPLBCounters(ctx context.Context, client grpcapi.MaglevClient, _ []
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(resp)
|
||||||
|
}
|
||||||
|
|
||||||
if len(resp.Vips) == 0 {
|
if len(resp.Vips) == 0 {
|
||||||
fmt.Println("(no counters — VPP disconnected or scrape pending)")
|
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]
|
name := args[0]
|
||||||
req.FrontendName = &name
|
req.FrontendName = &name
|
||||||
}
|
}
|
||||||
if _, err := client.SyncVPPLBState(ctx, req); err != nil {
|
_, err := client.SyncVPPLBState(ctx, req)
|
||||||
return err
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runShowVersion(_ context.Context, _ grpcapi.MaglevClient, _ []string) error {
|
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",
|
fmt.Printf("maglevc %s (commit %s, built %s)\n",
|
||||||
buildinfo.Version(), buildinfo.Commit(), buildinfo.Date())
|
buildinfo.Version(), buildinfo.Commit(), buildinfo.Date())
|
||||||
return nil
|
return nil
|
||||||
@@ -445,6 +457,9 @@ func runShowFrontends(ctx context.Context, client grpcapi.MaglevClient, _ []stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(resp)
|
||||||
|
}
|
||||||
for _, name := range resp.FrontendNames {
|
for _, name := range resp.FrontendNames {
|
||||||
fmt.Println(name)
|
fmt.Println(name)
|
||||||
}
|
}
|
||||||
@@ -461,6 +476,9 @@ func runShowFrontend(ctx context.Context, client grpcapi.MaglevClient, args []st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(info)
|
||||||
|
}
|
||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
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("name"), info.Name)
|
||||||
@@ -522,6 +540,9 @@ func runShowBackends(ctx context.Context, client grpcapi.MaglevClient, _ []strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(resp)
|
||||||
|
}
|
||||||
for _, name := range resp.BackendNames {
|
for _, name := range resp.BackendNames {
|
||||||
fmt.Println(name)
|
fmt.Println(name)
|
||||||
}
|
}
|
||||||
@@ -538,6 +559,9 @@ func runShowBackend(ctx context.Context, client grpcapi.MaglevClient, args []str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(info)
|
||||||
|
}
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
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("name"), info.Name)
|
||||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("address"), info.Address)
|
_, _ = 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(resp)
|
||||||
|
}
|
||||||
for _, name := range resp.Names {
|
for _, name := range resp.Names {
|
||||||
fmt.Println(name)
|
fmt.Println(name)
|
||||||
}
|
}
|
||||||
@@ -593,6 +620,9 @@ func runShowHealthCheck(ctx context.Context, client grpcapi.MaglevClient, args [
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(info)
|
||||||
|
}
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
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("name"), info.Name)
|
||||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cli.Label("type"), info.Type)
|
_, _ = 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)
|
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
info, err := client.PauseBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
_, err := client.PauseBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("%s: setting state to '%s'\n", info.Name, info.State)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runResumeBackend(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
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)
|
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
info, err := client.ResumeBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
_, err := client.ResumeBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("%s: setting state to '%s'\n", info.Name, info.State)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runSetFrontendPoolBackendWeight(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
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)
|
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
info, err := client.SetFrontendPoolBackendWeight(ctx, &grpcapi.SetWeightRequest{
|
_, err = client.SetFrontendPoolBackendWeight(ctx, &grpcapi.SetWeightRequest{
|
||||||
Frontend: frontendName,
|
Frontend: frontendName,
|
||||||
Pool: poolName,
|
Pool: poolName,
|
||||||
Backend: backendName,
|
Backend: backendName,
|
||||||
Weight: int32(weight),
|
Weight: int32(weight),
|
||||||
Flush: flush,
|
Flush: flush,
|
||||||
})
|
})
|
||||||
if err != nil {
|
return err
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runEnableBackend(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
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)
|
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
info, err := client.EnableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
_, err := client.EnableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("%s: enabled, state is '%s'\n", info.Name, info.State)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDisableBackend(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
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)
|
ctx, cancel := context.WithTimeout(ctx, callTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
info, err := client.DisableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
_, err := client.DisableBackend(ctx, &grpcapi.BackendRequest{Name: args[0]})
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("%s: disabled, state is '%s'\n", info.Name, info.State)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runConfigCheck(ctx context.Context, client grpcapi.MaglevClient, _ []string) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cli.IsJSON() {
|
||||||
|
return emitProto(resp)
|
||||||
|
}
|
||||||
if resp.Ok {
|
if resp.Ok {
|
||||||
fmt.Println("config ok")
|
fmt.Println("config ok")
|
||||||
return nil
|
return nil
|
||||||
@@ -764,7 +761,6 @@ func runConfigReload(ctx context.Context, client grpcapi.MaglevClient, _ []strin
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if resp.Ok {
|
if resp.Ok {
|
||||||
fmt.Println("config reloaded")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if resp.ParseError != "" {
|
if resp.ParseError != "" {
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
cli "git.ipng.ch/ipng/golang-cli"
|
||||||
|
"git.ipng.ch/ipng/vpp-maglev/internal/grpcapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSON output (-json). The protobuf response is the model: in JSON mode each
|
||||||
|
// command emits it via the shared cli.EmitJSON (protojson field names/enums); in
|
||||||
|
// text mode it keeps its curated tabwriter painter (which robot tests parse, and
|
||||||
|
// which carries column alignment JSON does not need). A few commands print only
|
||||||
|
// a confirmation in text mode — those emit the returned proto in JSON, or, when
|
||||||
|
// there is nothing to return, rely on wrapJSON to print "{}". Errors render as
|
||||||
|
// {"error": "..."} (see color.go).
|
||||||
|
|
||||||
|
// jsonEmitted records whether the current command produced JSON output of its
|
||||||
|
// own. Commands run serially, so a package var is safe. wrapJSON resets it
|
||||||
|
// before each command and appends "{}" for a successful command that produced
|
||||||
|
// nothing.
|
||||||
|
var jsonEmitted bool
|
||||||
|
|
||||||
|
// emitProto emits one proto message as JSON via the shared renderer.
|
||||||
|
func emitProto(m proto.Message) error {
|
||||||
|
raw, err := protoField(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
jsonEmitted = true
|
||||||
|
return cli.EmitJSON(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitJSON emits a composite value (maps/structs) as JSON.
|
||||||
|
func emitJSON(v any) error {
|
||||||
|
jsonEmitted = true
|
||||||
|
return cli.EmitJSON(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoField marshals a proto message to compact JSON (proto field names and
|
||||||
|
// enum spellings).
|
||||||
|
func protoField(m proto.Message) (json.RawMessage, error) {
|
||||||
|
b, err := protojson.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("marshal json: %w", err)
|
||||||
|
}
|
||||||
|
return json.RawMessage(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapJSON decorates every runnable node so that, in -json mode, a command that
|
||||||
|
// succeeds without emitting output of its own (e.g. sync) still produces "{}",
|
||||||
|
// giving every command a uniform JSON contract. Commands that show or return
|
||||||
|
// data emit it (setting jsonEmitted); errors propagate to App.FormatError, which
|
||||||
|
// renders them as {"error": "..."}. In text mode the wrapper is a no-op.
|
||||||
|
func wrapJSON(root *node) {
|
||||||
|
seen := map[*node]bool{}
|
||||||
|
var walk func(*node)
|
||||||
|
walk = func(n *node) {
|
||||||
|
if n == nil || seen[n] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen[n] = true
|
||||||
|
if n.Run != nil {
|
||||||
|
orig := n.Run
|
||||||
|
n.Run = func(ctx context.Context, c grpcapi.MaglevClient, a []string) error {
|
||||||
|
jsonEmitted = false
|
||||||
|
err := orig(ctx, c, a)
|
||||||
|
if err == nil && cli.IsJSON() && !jsonEmitted {
|
||||||
|
return emitJSON(struct{}{})
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ch := range n.Children {
|
||||||
|
walk(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walk(root)
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ func main() {
|
|||||||
Date: buildinfo.Date(),
|
Date: buildinfo.Date(),
|
||||||
Prompt: "maglev> ",
|
Prompt: "maglev> ",
|
||||||
Root: buildTree(),
|
Root: buildTree(),
|
||||||
|
JSON: true, // commands render via cli.IsJSON() (see json.go)
|
||||||
DefaultServer: "localhost:9090",
|
DefaultServer: "localhost:9090",
|
||||||
ServerEnv: "MAGLEV_SERVER",
|
ServerEnv: "MAGLEV_SERVER",
|
||||||
Connect: connect,
|
Connect: connect,
|
||||||
|
|||||||
+2
-1
@@ -26,7 +26,8 @@ func dynWatchEventOpts(_ context.Context, _ grpcapi.MaglevClient, _ []string) []
|
|||||||
// All tokens after 'events' are captured as args by the circular slot node in the tree.
|
// All tokens after 'events' are captured as args by the circular slot node in the tree.
|
||||||
// If none of log/backend/frontend are mentioned, all three default to true.
|
// If none of log/backend/frontend are mentioned, all three default to true.
|
||||||
func runWatchEvents(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
func runWatchEvents(ctx context.Context, client grpcapi.MaglevClient, args []string) error {
|
||||||
var maxEvents int // 0 = unlimited
|
jsonEmitted = true // watch streams its own JSON event lines; never append "{}"
|
||||||
|
var maxEvents int // 0 = unlimited
|
||||||
var wantLog, wantBackend, wantFrontend bool
|
var wantLog, wantBackend, wantFrontend bool
|
||||||
logLevel := ""
|
logLevel := ""
|
||||||
anyExplicit := false
|
anyExplicit := false
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module git.ipng.ch/ipng/vpp-maglev
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.ipng.ch/ipng/golang-cli v1.3.0
|
git.ipng.ch/ipng/golang-cli v1.4.0
|
||||||
github.com/charmbracelet/bubbletea v1.3.10
|
github.com/charmbracelet/bubbletea v1.3.10
|
||||||
github.com/charmbracelet/lipgloss v1.1.0
|
github.com/charmbracelet/lipgloss v1.1.0
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
git.ipng.ch/ipng/golang-cli v1.3.0 h1:E26W55czJSl+kmSXpwFWxYHsnaT84unsNBtfdM3iT4M=
|
git.ipng.ch/ipng/golang-cli v1.3.0 h1:E26W55czJSl+kmSXpwFWxYHsnaT84unsNBtfdM3iT4M=
|
||||||
git.ipng.ch/ipng/golang-cli v1.3.0/go.mod h1:8yeV4X7MF5hKQnxnYYJKyqby4P58EtH82zd36BrNbqY=
|
git.ipng.ch/ipng/golang-cli v1.3.0/go.mod h1:8yeV4X7MF5hKQnxnYYJKyqby4P58EtH82zd36BrNbqY=
|
||||||
|
git.ipng.ch/ipng/golang-cli v1.4.0 h1:KpvVqkieYoH3en7ay0QGmagiYcXJoUAiU49OLx3eZGw=
|
||||||
|
git.ipng.ch/ipng/golang-cli v1.4.0/go.mod h1:8yeV4X7MF5hKQnxnYYJKyqby4P58EtH82zd36BrNbqY=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
|||||||
Reference in New Issue
Block a user