From c3955fb3afa063c4d35a06bccd0629a688a23a1d Mon Sep 17 00:00:00 2001 From: "R.I.Pienaar" Date: Wed, 22 Feb 2023 12:42:46 +0100 Subject: [PATCH] (#81) expand the state admin tool Now shows all matching entries and include filters based on time, regex, advised etc to select those nodes. Signed-off-by: R.I.Pienaar --- cmd/cmd.go | 150 +++++++++++++++++++++++++++++++++++++++++++++-------- go.mod | 6 +-- go.sum | 12 ++--- 3 files changed, 137 insertions(+), 31 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index e28c0dc..f223ffa 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -15,6 +15,7 @@ import ( "os" "os/signal" "path/filepath" + "regexp" "runtime" "runtime/pprof" "strings" @@ -50,8 +51,11 @@ type cmd struct { findSince time.Duration findFollow bool nCtx string - stateDir string - stateValue string + stateSource string + stateValue *regexp.Regexp + stateValuesOnly bool + stateAdvised bool + stateSince time.Duration json bool choriaToken string choriaSeed string @@ -89,8 +93,12 @@ func Run() { admFind.Flag("choria-collective", "The Choria collective you will be connecting to").Default("choria").StringVar(&c.choriaCollective) admState := admin.Commandf("state", "Search state files").Action(c.findState) - admState.Arg("dir", "Directory where state files are kept").Required().ExistingDirVar(&c.stateDir) - admState.Arg("value", "The value to search for").Required().StringVar(&c.stateValue) + admState.Arg("dir", "Directory where state files are kept").Required().StringVar(&c.stateSource) + admState.Flag("since", "List only entries seen since a certain duration ago").DurationVar(&c.stateSince) + admState.Flag("advised", "Include entries that are in warning state").Default("true").BoolVar(&c.stateAdvised) + admState.Flag("value", "A regular expression value to search for").RegexpVar(&c.stateValue) + admState.Flag("values", "List only values rather than full entries").BoolVar(&c.stateValuesOnly) + admState.Flag("json", "Render JSON values").BoolVar(&c.json) admGossip := admin.Commandf("gossip", "View the synchronization traffic").Action(c.gossipAction) admGossip.Flag("json", "Render JSON values").BoolVar(&c.json) @@ -102,8 +110,10 @@ func Run() { app.MustParseWithUsage(os.Args[1:]) } -func (c *cmd) findState(_ *fisk.ParseContext) error { - return filepath.WalkDir(c.stateDir, func(path string, d fs.DirEntry, err error) error { +func (c *cmd) findStateFiles(dir string) ([]string, error) { + var paths []string + + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -116,6 +126,34 @@ func (c *cmd) findState(_ *fisk.ParseContext) error { return nil } + paths = append(paths, path) + + return nil + }) + + return paths, err +} + +func (c *cmd) findState(_ *fisk.ParseContext) error { + var paths []string + var err error + + if stat, err := os.Stat(c.stateSource); err == nil { + if !stat.IsDir() { + paths = append(paths, c.stateSource) + } + } + + if len(paths) == 0 { + paths, err = c.findStateFiles(c.stateSource) + if err != nil { + return err + } + } + + var selected []*idtrack.Item + + for _, path := range paths { items := idtrack.Tracker{} sb, err := os.ReadFile(path) if err != nil { @@ -126,30 +164,98 @@ func (c *cmd) findState(_ *fisk.ParseContext) error { return err } - item, ok := items.Items[c.stateValue] - if ok { - fmt.Printf("%s:\n\n", path) + for v, item := range items.Items { + item.Value = v - fmt.Printf(" Value: %v\n", c.stateValue) - if item.Seen.IsZero() { - fmt.Printf(" Seen Time: never\n") - } else { - fmt.Printf(" Seen Time: %v (%v)\n", item.Seen, time.Since(item.Seen).Round(time.Second)) + if c.stateSince > 0 && time.Since(item.Seen) >= c.stateSince { + continue } - if item.Copied.IsZero() { - fmt.Printf(" Copied Time: never\n") - } else { - fmt.Printf(" Copied Time: %v (%v)\n", item.Copied, time.Since(item.Copied).Round(time.Second)) + if !c.stateAdvised && item.Advised { + continue + } + + if c.stateValue != nil && !c.stateValue.MatchString(item.Value) { + continue } - fmt.Printf(" Payload Size: %v\n", item.Size) - fmt.Printf(" Advised: %t\n", item.Advised) - fmt.Println() + selected = append(selected, item) } + } + if len(selected) == 0 { + if c.stateValuesOnly { + return nil + } + if c.json { + fmt.Println("[]") + return nil + } + + fmt.Println("No items matched") return nil - }) + } + + // just values used later should we need to json dump + var values []string + + // avoid duplicate names though in practice one would partition on fqdn so there wouldnt be dupes but worth doing anyway + if c.stateValuesOnly { + uniq := []*idtrack.Item{} + seen := make(map[string]struct{}) + + for _, item := range selected { + if _, ok := seen[item.Value]; ok { + continue + } + + uniq = append(uniq, item) + values = append(values, item.Value) + seen[item.Value] = struct{}{} + } + + selected = uniq + } + + if c.json { + v := any(selected) + if c.stateValuesOnly { + v = values + } + + j, err := json.Marshal(v) + if err != nil { + return err + } + fmt.Println(string(j)) + return nil + } + + for _, item := range selected { + if c.stateValuesOnly { + fmt.Println(item.Value) + continue + } + + fmt.Printf(" Value: %v\n", item.Value) + if item.Seen.IsZero() { + fmt.Printf(" Seen Time: never\n") + } else { + fmt.Printf(" Seen Time: %v (%v)\n", item.Seen, time.Since(item.Seen).Round(time.Second)) + } + + if item.Copied.IsZero() { + fmt.Printf(" Copied Time: never\n") + } else { + fmt.Printf(" Copied Time: %v (%v)\n", item.Copied, time.Since(item.Copied).Round(time.Second)) + } + fmt.Printf(" Payload Size: %v\n", item.Size) + fmt.Printf(" Advised: %t\n", item.Advised) + + fmt.Println() + } + + return nil } func (c *cmd) connect() (*nats.Conn, error) { diff --git a/go.mod b/go.mod index 583e4c4..554b5ac 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/nats-io/jsm.go v0.0.35 github.com/nats-io/nats-server/v2 v2.9.14 github.com/nats-io/nats.go v1.23.0 - github.com/onsi/ginkgo/v2 v2.8.1 - github.com/onsi/gomega v1.26.0 + github.com/onsi/ginkgo/v2 v2.8.3 + github.com/onsi/gomega v1.27.1 github.com/prometheus/client_golang v1.14.0 github.com/segmentio/ksuid v1.0.4 github.com/sirupsen/logrus v1.9.0 @@ -24,7 +24,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/golang-jwt/jwt/v4 v4.4.3 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect diff --git a/go.sum b/go.sum index 1b571bd..73f42b7 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -57,10 +57,10 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/onsi/ginkgo/v2 v2.8.3 h1:RpbK1G8nWPNaCVFBWsOGnEQQGgASi6b8fxcWBvDYjxQ= +github.com/onsi/ginkgo/v2 v2.8.3/go.mod h1:6OaUA8BCi0aZfmzYT/q9AacwTzDpNbxILUT+TlBq6MY= +github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= +github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=