Skip to content

Commit

Permalink
Support filtering connections using expr in report connz
Browse files Browse the repository at this point in the history
Signed-off-by: R.I.Pienaar <[email protected]>
  • Loading branch information
ripienaar committed Sep 22, 2023
1 parent 455bfeb commit c705e84
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 12 deletions.
3 changes: 3 additions & 0 deletions cli/cheats/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ nats server report connz --account WEATHER
nats server report connz --sort in-msgs
nats server report connz --top 10 --sort in-msgs

# To limit connections report to surveyor connections and all from a specific IP using https://expr.medv.io/docs/Language-Definition
nats server report connz --filter 'lower(conns.name) matches "surveyor" || conns.ip == "46.101.44.80"'

# To report on accounts
nats server report accounts
nats server report accounts --account WEATHER --sort in-msgs --top 10
Expand Down
56 changes: 44 additions & 12 deletions cli/server_report_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"sort"

"github.com/antonmedv/expr"
"github.com/choria-io/fisk"
"github.com/dustin/go-humanize"
"github.com/fatih/color"
Expand All @@ -28,16 +29,17 @@ import (
type SrvReportCmd struct {
json bool

account string
waitFor int
sort string
topk int
reverse bool
compact bool
subject string
server string
cluster string
tags []string
filterExpression string
account string
waitFor int
sort string
topk int
reverse bool
compact bool
subject string
server string
cluster string
tags []string
}

type srvReportAccountInfo struct {
Expand Down Expand Up @@ -72,6 +74,7 @@ func configureServerReportCommand(srv *fisk.CmdClause) {
conns.Flag("top", "Limit results to the top results").Default("1000").IntVar(&c.topk)
conns.Flag("subject", "Limits responses only to those connections with matching subscription interest").StringVar(&c.subject)
conns.Flag("json", "Produce JSON output").Short('j').UnNegatableBoolVar(&c.json)
conns.Flag("filter", "Expression based filter for connections").StringVar(&c.filterExpression)

acct := report.Command("accounts", "Report on account activity").Alias("acct").Action(c.reportAccount)
acct.Arg("account", "Account to produce a report for").StringVar(&c.account)
Expand Down Expand Up @@ -492,7 +495,7 @@ func (c *SrvReportCmd) reportConnections(_ *fisk.ParseContext) error {
return fmt.Errorf("did not get results from any servers")
}

conns := connz.flatConnInfo()
conns := connz.flatConnInfo(c.filterExpression)

if c.json {
printJSON(conns)
Expand Down Expand Up @@ -624,10 +627,39 @@ func (c *SrvReportCmd) renderConnections(report []connInfo) {

type connzList []*server.ServerAPIConnzResponse

func (c connzList) flatConnInfo() []connInfo {
func (c connzList) flatConnInfo(filter string) []connInfo {
var conns []connInfo
for _, conn := range c {
srv := structWithoutOmitEmpty(*conn.Server)

for _, c := range conn.Data.Conns {
if filter != "" {
ci := structWithoutOmitEmpty(*c)
env := map[string]any{
"server": srv,
"Server": conn.Server,
"conns": ci,
"Conns": c,
}

program, err := expr.Compile(filter, expr.Env(env), expr.AsBool())
fisk.FatalIfError(err, "Invalid expression: %v", err)

out, err := expr.Run(program, env)
if err != nil {
fisk.FatalIfError(err, "Invalid expression: %v", err)
}

should, ok := out.(bool)
if !ok {
fisk.FatalIfError(err, "expression did not return a boolean")
}

if !should {
continue
}
}

conns = append(conns, connInfo{c, conn.Server})
}
}
Expand Down
36 changes: 36 additions & 0 deletions cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"net/textproto"
"os"
"os/exec"
"reflect"
"regexp"
"sort"
"strconv"
Expand Down Expand Up @@ -1324,3 +1325,38 @@ func filterDataThroughCmd(data []byte, filter, subject, stream string) ([]byte,
// maybe we want to do something on error?
return runner.CombinedOutput()
}

// given a non pointer instance of a type with a lot of omitempty json tags will return a new instance without those
//
// does not handle nested values
func structWithoutOmitEmpty(s any) any {
st := reflect.TypeOf(s)

// It's a pointer struct, convert to the value that it points to.
if st.Kind() == reflect.Ptr {
st = st.Elem()
}

fs := []reflect.StructField{}
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
field.Tag = reflect.StructTag(strings.ReplaceAll(string(field.Tag), ",omitempty", ""))
fs = append(fs, field)
}

st2 := reflect.StructOf(fs)
v := reflect.ValueOf(s)

j, err := json.Marshal(v.Convert(st2).Interface())
if err != nil {
panic(err)
}

var res any
err = json.Unmarshal(j, &res)
if err != nil {
panic(err)
}

return res
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ require (
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/sevlyar/retag v0.0.0-20190429052747-c3f10e304082 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sevlyar/retag v0.0.0-20190429052747-c3f10e304082 h1:fj05fHX+p6w6xqPfvEjFtdu95JwguF0Kg1cz/sht8+U=
github.com/sevlyar/retag v0.0.0-20190429052747-c3f10e304082/go.mod h1:mOWh3Kdot9kBKCLbKcJTzIBBEPKRJAq2lk03eVVDmco=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down

0 comments on commit c705e84

Please sign in to comment.