diff --git a/LOCAL_DEVELOPMENT.md b/LOCAL_DEVELOPMENT.md index 98ce95f3..02e1194f 100644 --- a/LOCAL_DEVELOPMENT.md +++ b/LOCAL_DEVELOPMENT.md @@ -98,11 +98,11 @@ JetStream Account Information: The server sets up 3 accounts: -|Account|Description| -|-------|-----------| -|USER |General access for local development| -|SERVICE|Account for a service with wildcard imports/exports| -|SYSTEM |Account for monitoring purposes| +| Account | Description | +|---------|-----------------------------------------------------| +| USER | General access for local development | +| SERVICE | Account for a service with wildcard imports/exports | +| SYSTEM | Account for monitoring purposes | From the `SERVICE` account we export subjects `service.>` and these are imported into the `USER` account on `imports.SERVICE.>`. diff --git a/README.md b/README.md index d4a29053..9fe1db97 100644 --- a/README.md +++ b/README.md @@ -811,11 +811,10 @@ this number of operations will result in a critical error sumular to the lag on Sources and Mirrors but checks the lag in the Raft cluster. `--msgs-warn=MSGS` and `--msgs-critical=MSGS` Checks the number of messages in the stream, if warn is smaller than -critical the check will alert for fewer messages than the thresholds. If warn is bigger than critical the logic will be -inverted ensuring that no more than the thresholds exist in the stream. +critical the check will alert for fewer messages than the thresholds. -`--subjects-warn=SUBJECTS` and `--subjects-critical=SUBJECTS` Checks the number of subjects in the stream, supports the -same inversion behaviour described above in `--msgs-warn`. +`--subjects-warn=SUBJECTS` and `--subjects-critical=SUBJECTS` Checks the number of subjects in the stream. If warn is +bigger than critical the logic will be inverted ensuring that no more than the thresholds exist in the stream. ##### Consumers diff --git a/cli/object_command.go b/cli/object_command.go index fbc923f9..27721df4 100644 --- a/cli/object_command.go +++ b/cli/object_command.go @@ -16,7 +16,6 @@ package cli import ( "encoding/base64" "fmt" - "github.com/nats-io/natscli/internal/util" "io" "os" "path/filepath" @@ -24,6 +23,8 @@ import ( "strings" "time" + "github.com/nats-io/natscli/internal/util" + "github.com/AlecAivazis/survey/v2" "github.com/choria-io/fisk" "github.com/dustin/go-humanize" @@ -79,7 +80,7 @@ NOTE: This is an experimental feature. add.Flag("storage", "Storage backend to use (file, memory)").EnumVar(&c.storage, "file", "f", "memory", "m") add.Flag("tags", "Place the store on servers that has specific tags").StringsVar(&c.placementTags) add.Flag("cluster", "Place the store on a specific cluster").StringVar(&c.placementCluster) - add.Flag("metadata", "Adds metadata to the bucvket").PlaceHolder("META").StringMapVar(&c.metadata) + add.Flag("metadata", "Adds metadata to the bucket").PlaceHolder("META").StringMapVar(&c.metadata) add.Flag("compress", "Compress the bucket data").BoolVar(&c.compression) add.PreAction(c.parseLimitStrings) diff --git a/cli/server_check_command.go b/cli/server_check_command.go index 8d3b01b5..b9468514 100644 --- a/cli/server_check_command.go +++ b/cli/server_check_command.go @@ -26,10 +26,10 @@ import ( "github.com/choria-io/fisk" "github.com/nats-io/jsm.go" "github.com/nats-io/jsm.go/api" + "github.com/nats-io/jsm.go/monitor" "github.com/nats-io/jwt/v2" "github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nats.go" - "github.com/nats-io/natscli/monitor" "github.com/nats-io/nkeys" ) @@ -322,51 +322,6 @@ func (c *SrvCheckCmd) checkKVStatusAndBucket(check *monitor.Result, nc *nats.Con } } -func (c *SrvCheckCmd) checkConsumerStatus(check *monitor.Result, nfo api.ConsumerInfo) { - check.Pd(&monitor.PerfDataItem{Name: "ack_pending", Value: float64(nfo.NumAckPending), Help: "The number of messages waiting to be Acknowledged", Crit: float64(c.consumerAckOutstandingCritical)}) - check.Pd(&monitor.PerfDataItem{Name: "pull_waiting", Value: float64(nfo.NumWaiting), Help: "The number of waiting Pull requests", Crit: float64(c.consumerWaitingCritical)}) - check.Pd(&monitor.PerfDataItem{Name: "pending", Value: float64(nfo.NumPending), Help: "The number of messages that have not yet been consumed", Crit: float64(c.consumerUnprocessedCritical)}) - check.Pd(&monitor.PerfDataItem{Name: "redelivered", Value: float64(nfo.NumRedelivered), Help: "The number of messages currently being redelivered", Crit: float64(c.consumerRedeliveryCritical)}) - if nfo.Delivered.Last != nil { - check.Pd(&monitor.PerfDataItem{Name: "last_delivery", Value: time.Since(*nfo.Delivered.Last).Seconds(), Unit: "s", Help: "Seconds since the last message was delivered", Crit: c.consumerLastDeliveryCritical.Seconds()}) - } - if nfo.AckFloor.Last != nil { - check.Pd(&monitor.PerfDataItem{Name: "last_ack", Value: time.Since(*nfo.AckFloor.Last).Seconds(), Unit: "s", Help: "Seconds since the last message was acknowledged", Crit: c.consumerLastDeliveryCritical.Seconds()}) - } - - if c.consumerAckOutstandingCritical > 0 && nfo.NumAckPending >= c.consumerAckOutstandingCritical { - check.Critical("Ack Pending: %d", nfo.NumAckPending) - } - - if c.consumerWaitingCritical > 0 && nfo.NumWaiting >= c.consumerWaitingCritical { - check.Critical("Waiting Pulls: %d", nfo.NumWaiting) - } - - if c.consumerUnprocessedCritical > 0 && nfo.NumPending >= uint64(c.consumerUnprocessedCritical) { - check.Critical("Unprocessed Messages: %d", nfo.NumPending) - } - - if c.consumerRedeliveryCritical > 0 && nfo.NumRedelivered > c.consumerRedeliveryCritical { - check.Critical("Redelivered Messages: %d", nfo.NumRedelivered) - } - - switch { - case c.consumerLastDeliveryCritical <= 0: - case nfo.Delivered.Last == nil: - check.Critical("No deliveries") - case time.Since(*nfo.Delivered.Last) >= c.consumerLastDeliveryCritical: - check.Critical("Last delivery %v ago", time.Since(*nfo.Delivered.Last).Round(time.Second)) - } - - switch { - case c.consumerLastAckCritical <= 0: - case nfo.AckFloor.Last == nil: - check.Critical("No acknowledgements") - case time.Since(*nfo.AckFloor.Last) >= c.consumerLastAckCritical: - check.Critical("Last ack %v ago", time.Since(*nfo.AckFloor.Last).Round(time.Second)) - } -} - func (c *SrvCheckCmd) checkConsumer(_ *fisk.ParseContext) error { check := &monitor.Result{Name: fmt.Sprintf("%s_%s", c.sourcesStream, c.consumerName), Check: "consumer", OutFile: checkRenderOutFile, NameSpace: opts().PrometheusNamespace, RenderFormat: checkRenderFormat} defer check.GenericExit() @@ -380,20 +335,40 @@ func (c *SrvCheckCmd) checkConsumer(_ *fisk.ParseContext) error { return nil } - nfo, err := cons.LatestState() - if err != nil { - check.Critical("consumer state failure: %v", err) - return nil - } - + checkOpts := &jsm.ConsumerHealthCheckOptions{} if c.useMetadata { - err = c.optionsFromConsumerMetadata(&nfo.Config) + checkOpts, err = cons.MonitorOptions() if err != nil { - return err + return fmt.Errorf("invalid metadata: %v", err) } } + if !c.useMetadata || c.consumerAckOutstandingCriticalIsSet { + checkOpts.AckOutstandingCritical = c.consumerAckOutstandingCritical + } + if !c.useMetadata || c.consumerWaitingCriticalIsSet { + checkOpts.WaitingCritical = c.consumerWaitingCritical + } + if !c.useMetadata || c.consumerUnprocessedCriticalIsSet { + checkOpts.UnprocessedCritical = c.consumerUnprocessedCritical + } + if !c.useMetadata || c.consumerLastDeliveryCriticalIsSet { + checkOpts.LastDeliveryCritical = c.consumerLastDeliveryCritical + } + if !c.useMetadata || c.consumerLastAckCriticalIsSet { + checkOpts.LastAckCritical = c.consumerLastAckCritical + } + if !c.useMetadata || c.consumerRedeliveryCriticalIsSet { + checkOpts.RedeliveryCritical = c.consumerRedeliveryCritical + } - c.checkConsumerStatus(check, nfo) + logger := api.NewDiscardLogger() + if opts().Trace { + logger = api.NewDefaultLogger(api.TraceLevel) + } + _, err = cons.HealthCheck(*checkOpts, check, logger) + if err != nil { + return fmt.Errorf("health check failed: %v", err) + } return nil } @@ -866,132 +841,6 @@ func (c *SrvCheckCmd) checkClusterInfo(check *monitor.Result, ci *server.Cluster return nil } -func (c *SrvCheckCmd) optionsFromConsumerMetadata(cfg *api.ConsumerConfig) error { - var err error - - var meta = []struct { - k string - skip bool - fn func(string) error - }{ - {"io.nats.monitor.outstanding-ack-critical", c.consumerAckOutstandingCriticalIsSet, func(v string) error { - c.consumerAckOutstandingCritical, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.waiting-critical", c.consumerWaitingCriticalIsSet, func(v string) error { - c.consumerWaitingCritical, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.unprocessed-critical", c.consumerUnprocessedCriticalIsSet, func(v string) error { - c.consumerUnprocessedCritical, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.last-delivery-critical", c.consumerLastDeliveryCriticalIsSet, func(v string) error { - c.consumerLastDeliveryCritical, err = fisk.ParseDuration(v) - return err - }}, - {"io.nats.monitor.last-ack-critical", c.consumerLastAckCriticalIsSet, func(v string) error { - c.consumerLastAckCritical, err = fisk.ParseDuration(v) - return err - }}, - {"io.nats.monitor.redelivery-critical", c.consumerRedeliveryCriticalIsSet, func(v string) error { - c.consumerRedeliveryCritical, err = strconv.Atoi(v) - return err - }}, - } - - for _, m := range meta { - if m.skip { - continue - } - - if v, ok := cfg.Metadata[m.k]; ok { - if opts().Trace { - fmt.Printf(">>> Setting thresholds based on metadata: %v: %v\n", m.k, v) - } - err = m.fn(v) - if err != nil { - return fmt.Errorf("invalid metadata: %s: %s", m.k, err) - } - } - } - - return nil -} - -func (c *SrvCheckCmd) optionsFromStreamMetadata(cfg *api.StreamConfig) error { - var err error - - var meta = []struct { - k string - skip bool - fn func(string) error - }{ - {"io.nats.monitor.lag-critical", c.sourcesLagCriticalIsSet, func(v string) error { - c.sourcesLagCritical, err = strconv.ParseUint(v, 10, 64) - return err - }}, - {"io.nats.monitor.seen-critical", c.sourcesSeenCriticalIsSet, func(v string) error { - c.sourcesSeenCritical, err = fisk.ParseDuration(v) - return err - }}, - {"io.nats.monitor.min-sources", c.sourcesMinSourcesIsSet, func(v string) error { - c.sourcesMinSources, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.max-sources", c.sourcesMaxSourcesIsSet, func(v string) error { - c.sourcesMaxSources, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.peer-expect", c.raftExpectIsSet, func(v string) error { - c.raftExpect, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.peer-lag-critical", c.raftLagCriticalIsSet, func(v string) error { - c.raftLagCritical, err = strconv.ParseUint(v, 10, 64) - return err - }}, - {"io.nats.monitor.peer-seen-critical", c.raftSeenCriticalIsSet, func(v string) error { - c.raftSeenCritical, err = fisk.ParseDuration(v) - return err - }}, - {"io.nats.monitor.msgs-warn", c.streamMessagesWarnIsSet, func(v string) error { - c.streamMessagesWarn, err = strconv.ParseUint(v, 10, 64) - return err - }}, - {"io.nats.monitor.msgs-critical", c.streamMessagesCritIsSet, func(v string) error { - c.streamMessagesCrit, err = strconv.ParseUint(v, 10, 64) - return err - }}, - {"io.nats.monitor.subjects-warn", c.subjectsWarnIsSet, func(v string) error { - c.subjectsWarn, err = strconv.Atoi(v) - return err - }}, - {"io.nats.monitor.subjects-critical", c.subjectsCritIsSet, func(v string) error { - c.subjectsCrit, err = strconv.Atoi(v) - return err - }}, - } - - for _, m := range meta { - if m.skip { - continue - } - - if v, ok := cfg.Metadata[m.k]; ok { - if opts().Trace { - fmt.Printf(">>> Setting thresholds based on metadata: %v: %v\n", m.k, v) - } - err = m.fn(v) - if err != nil { - return fmt.Errorf("invalid metadata: %s: %s", m.k, err) - } - } - } - - return nil -} - func (c *SrvCheckCmd) checkStream(_ *fisk.ParseContext) error { check := &monitor.Result{Name: c.sourcesStream, Check: "stream", OutFile: checkRenderOutFile, NameSpace: opts().PrometheusNamespace, RenderFormat: checkRenderFormat} defer check.GenericExit() @@ -1002,136 +851,52 @@ func (c *SrvCheckCmd) checkStream(_ *fisk.ParseContext) error { stream, err := mgr.LoadStream(c.sourcesStream) check.CriticalIfErr(err, "could not load stream %s: %s", c.sourcesStream, err) - info, err := stream.LatestInformation() - check.CriticalIfErr(err, "could not load stream %s info: %s", c.sourcesStream, err) - + checkOpts := &jsm.StreamHealthCheckOptions{} if c.useMetadata { - err = c.optionsFromStreamMetadata(&info.Config) - if err != nil { - return err - } + checkOpts, err = stream.HealthCheckOptions() + check.CriticalIfErr(err, "Invalid metadata: %s", err) } - if info.Cluster != nil { - var sci server.ClusterInfo - cij, _ := json.Marshal(info.Cluster) - json.Unmarshal(cij, &sci) - err = c.checkClusterInfo(check, &sci) - check.CriticalIfErr(err, "Invalid cluster data: %s", err) - - if len(check.Criticals) == 0 { - check.Ok("%d current replicas", len(info.Cluster.Replicas)+1) - } - } else if c.raftExpect > 0 { - check.Critical("not clustered expected %d peers", c.raftExpect) + if !c.useMetadata || c.sourcesLagCriticalIsSet { + checkOpts.SourcesLagCritical = c.sourcesLagCritical } - - check.Pd(&monitor.PerfDataItem{Name: "messages", Value: float64(info.State.Msgs), Warn: float64(c.streamMessagesWarn), Crit: float64(c.streamMessagesCrit), Help: "Messages stored in the stream"}) - if c.streamMessagesWarn > 0 && info.State.Msgs <= c.streamMessagesWarn { - check.Warn("%d messages", info.State.Msgs) + if !c.useMetadata || c.sourcesSeenCriticalIsSet { + checkOpts.SourcesSeenCritical = c.sourcesSeenCritical } - if c.streamMessagesCrit > 0 && info.State.Msgs <= c.streamMessagesCrit { - check.Critical("%d messages", info.State.Msgs) + if !c.useMetadata || c.sourcesMinSourcesIsSet { + checkOpts.MinSources = c.sourcesMinSources } - - check.Pd(&monitor.PerfDataItem{Name: "subjects", Value: float64(info.State.NumSubjects), Warn: float64(c.subjectsWarn), Crit: float64(c.subjectsCrit), Help: "Number of subjects stored in the stream"}) - if c.subjectsWarn > 0 || c.subjectsCrit > 0 { - ns := info.State.NumSubjects - if c.subjectsWarn < c.subjectsCrit { // it means we're asserting that there are fewer subjects than thresholds - if ns >= c.subjectsCrit { - check.Critical("%d subjects", info.State.NumSubjects) - } else if ns >= c.subjectsWarn { - check.Warn("%d subjects", info.State.NumSubjects) - } - } else { // it means we're asserting that there are more subjects than thresholds - if ns <= c.subjectsCrit { - check.Critical("%d subjects", info.State.NumSubjects) - } else if ns <= c.subjectsWarn { - check.Warn("%d subjects", info.State.NumSubjects) - } - } + if !c.useMetadata || c.sourcesMaxSourcesIsSet { + checkOpts.MaxSources = c.sourcesMaxSources } - - switch { - case stream.IsMirror(): - err = c.checkMirror(check, info) - check.CriticalIfErr(err, "Invalid mirror data: %s", err) - - if len(check.Criticals) == 0 { - check.Ok("%s mirror of %s is %d lagged, last seen %s ago", c.sourcesStream, info.Mirror.Name, info.Mirror.Lag, info.Mirror.Active.Round(time.Millisecond)) - } - - case stream.IsSourced(): - err = c.checkSources(check, info) - check.CriticalIfErr(err, "Invalid source data: %s", err) - - if len(check.Criticals) == 0 { - check.Ok("%d sources", len(info.Sources)) - } + if !c.useMetadata || c.raftExpectIsSet { + checkOpts.ClusterExpectedPeers = c.raftExpect } - - return nil -} - -func (c *SrvCheckCmd) checkMirror(check *monitor.Result, info *api.StreamInfo) error { - if info.Mirror == nil { - check.Critical("not mirrored") - return nil + if !c.useMetadata || c.raftLagCriticalIsSet { + checkOpts.ClusterLagCritical = c.raftLagCritical } - - check.Pd( - &monitor.PerfDataItem{Name: "lag", Crit: float64(c.sourcesLagCritical), Value: float64(info.Mirror.Lag), Help: "Number of operations this peer is behind its origin"}, - &monitor.PerfDataItem{Name: "active", Crit: c.sourcesSeenCritical.Seconds(), Unit: "s", Value: info.Mirror.Active.Seconds(), Help: "Indicates if this peer is active and catching up if lagged"}, - ) - - if c.sourcesLagCritical > 0 && info.Mirror.Lag > c.sourcesLagCritical { - check.Critical("%d messages behind", info.Mirror.Lag) + if !c.useMetadata || c.raftSeenCriticalIsSet { + checkOpts.ClusterSeenCritical = c.raftSeenCritical } - - if c.sourcesSeenCritical > 0 && info.Mirror.Active > c.sourcesSeenCritical { - check.Critical("last active %s", info.Mirror.Active) + if !c.useMetadata || c.streamMessagesWarnIsSet { + checkOpts.MessagesWarn = c.streamMessagesWarn } - - return nil -} - -func (c *SrvCheckCmd) checkSources(check *monitor.Result, info *api.StreamInfo) error { - check.Pd(&monitor.PerfDataItem{Name: "sources", Value: float64(len(info.Sources)), Warn: float64(c.sourcesMinSources), Crit: float64(c.sourcesMaxSources), Help: "Number of sources being consumed by this stream"}) - - if len(info.Sources) == 0 { - check.Critical("no sources defined") + if !c.useMetadata || c.streamMessagesCritIsSet { + checkOpts.MessagesCrit = c.streamMessagesCrit } - - lagged := 0 - inactive := 0 - - for _, s := range info.Sources { - if c.sourcesLagCritical > 0 && s.Lag > c.sourcesLagCritical { - lagged++ - } - - if c.sourcesSeenCritical > 0 && s.Active > c.sourcesSeenCritical { - inactive++ - } - } - - check.Pd( - &monitor.PerfDataItem{Name: "sources_lagged", Value: float64(lagged), Help: "Number of sources that are behind more than the configured threshold"}, - &monitor.PerfDataItem{Name: "sources_inactive", Value: float64(inactive), Help: "Number of sources that are inactive"}, - ) - - if lagged > 0 { - check.Critical("%d lagged sources", lagged) + if !c.useMetadata || c.subjectsWarnIsSet { + checkOpts.SubjectsWarn = c.subjectsWarn } - if inactive > 0 { - check.Critical("%d inactive sources", inactive) - } - if len(info.Sources) < c.sourcesMinSources { - check.Critical("%d sources of min expected %d", len(info.Sources), c.sourcesMinSources) + if !c.useMetadata || c.subjectsCritIsSet { + checkOpts.SubjectsCrit = c.subjectsCrit } - if len(info.Sources) > c.sourcesMaxSources { - check.Critical("%d sources of max expected %d", len(info.Sources), c.sourcesMaxSources) + + logger := api.NewDiscardLogger() + if opts().Trace { + logger = api.NewDefaultLogger(api.TraceLevel) } + _, err = stream.HealthCheck(*checkOpts, check, logger) + check.CriticalIfErr(err, "Healthcheck failed: %s", err) return nil } diff --git a/cli/server_check_command_test.go b/cli/server_check_command_test.go index cac3b8ea..e940881d 100644 --- a/cli/server_check_command_test.go +++ b/cli/server_check_command_test.go @@ -15,7 +15,6 @@ package cli import ( "fmt" - "github.com/nats-io/natscli/options" "os" "strconv" "strings" @@ -23,10 +22,12 @@ import ( "time" "github.com/nats-io/jsm.go" + "github.com/nats-io/nats.go" + "github.com/nats-io/natscli/options" + "github.com/nats-io/jsm.go/api" + "github.com/nats-io/jsm.go/monitor" "github.com/nats-io/nats-server/v2/server" - "github.com/nats-io/nats.go" - "github.com/nats-io/natscli/monitor" ) func assertHasPDItem(t *testing.T, check *monitor.Result, items ...string) { @@ -81,158 +82,6 @@ func dfltCmd() *SrvCheckCmd { return &SrvCheckCmd{kvBucket: "TEST", kvValuesWarn: -1, kvValuesCrit: -1} } -func TestCheckConsumer(t *testing.T) { - t.Run("Ack Pending", func(t *testing.T) { - cmd := &SrvCheckCmd{sourcesStream: "TEST", consumerName: "CONS", consumerAckOutstandingCritical: 100} - check := &monitor.Result{} - - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumAckPending: 10, - }) - - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListIsEmpty(t, check.Criticals) - - check = &monitor.Result{} - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumAckPending: 300, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "Ack Pending: 300") - }) - - t.Run("Waiting Pulls", func(t *testing.T) { - cmd := &SrvCheckCmd{sourcesStream: "TEST", consumerName: "CONS", consumerWaitingCritical: 100} - check := &monitor.Result{} - - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumWaiting: 10, - }) - - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListIsEmpty(t, check.Criticals) - - check = &monitor.Result{} - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumWaiting: 300, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "Waiting Pulls: 300") - }) - - t.Run("Pending", func(t *testing.T) { - cmd := &SrvCheckCmd{sourcesStream: "TEST", consumerName: "CONS", consumerUnprocessedCritical: 100} - check := &monitor.Result{} - - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumPending: 10, - }) - - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListIsEmpty(t, check.Criticals) - - check = &monitor.Result{} - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumPending: 300, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "Unprocessed Messages: 300") - }) - - t.Run("Redelivered", func(t *testing.T) { - cmd := &SrvCheckCmd{sourcesStream: "TEST", consumerName: "CONS", consumerRedeliveryCritical: 100} - check := &monitor.Result{} - - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumRedelivered: 10, - }) - - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListIsEmpty(t, check.Criticals) - - check = &monitor.Result{} - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - NumRedelivered: 300, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "Redelivered Messages: 300") - }) - - t.Run("Last Delivery", func(t *testing.T) { - cmd := &SrvCheckCmd{sourcesStream: "TEST", consumerName: "CONS", consumerLastDeliveryCritical: 10 * time.Second} - check := &monitor.Result{} - - cmd.checkConsumerStatus(check, api.ConsumerInfo{}) - - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "No deliveries") - - check = &monitor.Result{} - last := time.Now().Add(-1 * time.Second) - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - Delivered: api.SequenceInfo{ - Last: &last, - }, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListIsEmpty(t, check.Criticals) - - check = &monitor.Result{} - last = time.Now().Add(-500 * time.Second) - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - Delivered: api.SequenceInfo{ - Last: &last, - }, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "Last delivery 8m20s ago") - }) - - t.Run("Last Ack", func(t *testing.T) { - cmd := &SrvCheckCmd{sourcesStream: "TEST", consumerName: "CONS", consumerLastAckCritical: 10 * time.Second} - check := &monitor.Result{} - - cmd.checkConsumerStatus(check, api.ConsumerInfo{}) - - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "No acknowledgements") - - check = &monitor.Result{} - last := time.Now().Add(-1 * time.Second) - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - AckFloor: api.SequenceInfo{ - Last: &last, - }, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListIsEmpty(t, check.Criticals) - - check = &monitor.Result{} - last = time.Now().Add(-500 * time.Second) - cmd.checkConsumerStatus(check, api.ConsumerInfo{ - AckFloor: api.SequenceInfo{ - Last: &last, - }, - }) - assertListIsEmpty(t, check.Warnings) - assertListIsEmpty(t, check.OKs) - assertListEquals(t, check.Criticals, "Last ack 8m20s ago") - }) -} - func TestCheckMessage(t *testing.T) { t.Run("Body timestamp", func(t *testing.T) { withJetStream(t, func(_ *server.Server, nc *nats.Conn, mgr *jsm.Manager) { @@ -538,113 +387,6 @@ func TestCheckAccountInfo(t *testing.T) { }) } -func TestCheckMirror(t *testing.T) { - cmd := &SrvCheckCmd{} - info := &api.StreamInfo{} - cmd.sourcesSeenCritical = time.Second - cmd.sourcesLagCritical = 50 - - t.Run("no mirror", func(t *testing.T) { - check := &monitor.Result{} - assertNoError(t, cmd.checkMirror(check, info)) - assertListEquals(t, check.Criticals, "not mirrored") - assertListIsEmpty(t, check.Warnings) - }) - - t.Run("failed mirror", func(t *testing.T) { - info.Mirror = &api.StreamSourceInfo{Name: "M", Lag: 100, Active: time.Hour} - check := &monitor.Result{} - err := cmd.checkMirror(check, info) - checkErr(t, err, "unexpected error") - assertHasPDItem(t, check, "lag=100;;50 active=3600.0000s;;1.0000") - assertListEquals(t, check.Criticals, "100 messages behind", "last active 1h0m0s") - assertListIsEmpty(t, check.Warnings) - }) - - t.Run("ok mirror", func(t *testing.T) { - info.Mirror = &api.StreamSourceInfo{Name: "M", Lag: 1, Active: 10 * time.Millisecond} - check := &monitor.Result{} - assertNoError(t, cmd.checkMirror(check, info)) - assertHasPDItem(t, check, "lag=1;;50 active=0.0100s;;1.0000") - assertListIsEmpty(t, check.Criticals) - assertListIsEmpty(t, check.Warnings) - }) -} - -func TestCheckSources(t *testing.T) { - cmd := &SrvCheckCmd{} - info := &api.StreamInfo{} - - t.Run("no sources", func(t *testing.T) { - check := &monitor.Result{} - assertNoError(t, cmd.checkSources(check, info)) - assertListEquals(t, check.Criticals, "no sources defined") - }) - - cmd.sourcesLagCritical = 10 - cmd.sourcesSeenCritical = time.Second - cmd.sourcesMaxSources = 10 - cmd.sourcesMinSources = 1 - - t.Run("lagged source", func(t *testing.T) { - info = &api.StreamInfo{ - Sources: []*api.StreamSourceInfo{ - {Name: "s1", Lag: 1000, Active: time.Millisecond}, - }, - } - - check := &monitor.Result{} - assertNoError(t, cmd.checkSources(check, info)) - assertListEquals(t, check.Criticals, "1 lagged sources") - assertHasPDItem(t, check, "sources=1;1;10", "sources_lagged=1", "sources_inactive=0") - }) - - t.Run("inactive source", func(t *testing.T) { - info = &api.StreamInfo{ - Sources: []*api.StreamSourceInfo{ - {Name: "s1", Active: time.Hour}, - }, - } - - check := &monitor.Result{} - assertNoError(t, cmd.checkSources(check, info)) - assertListEquals(t, check.Criticals, "1 inactive sources") - assertHasPDItem(t, check, "sources=1;1;10", "sources_lagged=0", "sources_inactive=1") - }) - - t.Run("not enough sources", func(t *testing.T) { - info = &api.StreamInfo{ - Sources: []*api.StreamSourceInfo{ - {Name: "s1", Active: time.Millisecond}, - }, - } - - cmd.sourcesMinSources = 2 - - check := &monitor.Result{} - assertNoError(t, cmd.checkSources(check, info)) - assertListEquals(t, check.Criticals, "1 sources of min expected 2") - assertHasPDItem(t, check, "sources=1;2;10", "sources_lagged=0", "sources_inactive=0") - }) - - t.Run("too many sources", func(t *testing.T) { - info = &api.StreamInfo{ - Sources: []*api.StreamSourceInfo{ - {Name: "s1", Active: time.Millisecond}, - {Name: "s2", Active: time.Millisecond}, - }, - } - - cmd.sourcesMinSources = 1 - cmd.sourcesMaxSources = 1 - - check := &monitor.Result{} - assertNoError(t, cmd.checkSources(check, info)) - assertListEquals(t, check.Criticals, "2 sources of max expected 1") - assertHasPDItem(t, check, "sources=2;1;1", "sources_lagged=0", "sources_inactive=0") - }) -} - func TestCheckVarz(t *testing.T) { t.Run("nil data", func(t *testing.T) { cmd := &SrvCheckCmd{srvName: "testing"} diff --git a/cli/server_cluster_command.go b/cli/server_cluster_command.go index 01d3cbc6..7293917d 100644 --- a/cli/server_cluster_command.go +++ b/cli/server_cluster_command.go @@ -84,12 +84,12 @@ func (c *SrvClusterCmd) balanceAction(_ *fisk.ParseContext) error { return err } - level := connbalancer.InfoLevel + level := api.InfoLevel if opts().Trace { - level = connbalancer.TraceLevel + level = api.TraceLevel } - balancer, err := connbalancer.New(nc, c.balanceRunTime, connbalancer.NewDefaultLogger(level), connbalancer.ConnectionSelector{ + balancer, err := connbalancer.New(nc, c.balanceRunTime, api.NewDefaultLogger(level), connbalancer.ConnectionSelector{ ServerName: c.balanceServerName, Idle: c.balanceIdle, Account: c.balanceAccount, diff --git a/cli/server_run_command.go b/cli/server_run_command.go index 9d1bd1b8..8099d723 100644 --- a/cli/server_run_command.go +++ b/cli/server_run_command.go @@ -16,7 +16,6 @@ package cli import ( "context" "fmt" - iu "github.com/nats-io/natscli/internal/util" "net" "net/url" "os" @@ -27,6 +26,8 @@ import ( "syscall" "text/template" + iu "github.com/nats-io/natscli/internal/util" + "github.com/choria-io/fisk" "github.com/nats-io/jsm.go/natscontext" "github.com/nats-io/nats-server/v2/server" @@ -175,7 +176,14 @@ func (c *SrvRunCmd) prepareConfig() error { return err } - c.config.Context = opts().Config + if c.config.ExtendWithContext { + c.config.Context, err = natscontext.New(c.config.Name, true) + if err != nil { + return err + } + } else { + c.config.Context = opts().Config + } if opts().Trace { c.config.Verbose = true diff --git a/cli/util.go b/cli/util.go index 211a26f3..9634c4cb 100644 --- a/cli/util.go +++ b/cli/util.go @@ -21,7 +21,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nats-io/natscli/options" "io" "math" "math/rand" @@ -37,6 +36,8 @@ import ( "time" "unicode" + "github.com/nats-io/natscli/options" + iu "github.com/nats-io/natscli/internal/util" "github.com/AlecAivazis/survey/v2" diff --git a/go.mod b/go.mod index 3ba9e8c0..5a5c3ce8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/choria-io/scaffold v0.0.2-0.20240516112801-fc127c79a1df github.com/dustin/go-humanize v1.0.1 github.com/emicklei/dot v1.6.2 - github.com/expr-lang/expr v1.16.7 + github.com/expr-lang/expr v1.16.9 github.com/fatih/color v1.17.0 github.com/ghodss/yaml v1.0.0 github.com/google/go-cmp v0.6.0 @@ -18,22 +18,20 @@ require ( github.com/guptarohit/asciigraph v0.7.1 github.com/jedib0t/go-pretty/v6 v6.5.9 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/klauspost/compress v1.17.8 + github.com/klauspost/compress v1.17.9 github.com/mattn/go-isatty v0.0.20 - github.com/nats-io/jsm.go v0.1.1-0.20240517153523-ca97f76bed5c + github.com/nats-io/jsm.go v0.1.1-0.20240621160505-af16666abb2e github.com/nats-io/jwt/v2 v2.5.7 github.com/nats-io/nats-server/v2 v2.11.0-preview.2 - github.com/nats-io/nats.go v1.35.0 + github.com/nats-io/nats.go v1.36.0 github.com/nats-io/nkeys v0.4.7 github.com/nats-io/nuid v1.0.1 - github.com/prometheus/client_golang v1.19.1 - github.com/prometheus/common v0.53.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240501200458-e2594dc0b29f github.com/tylertreat/hdrhistogram-writer v0.0.0-20210816161836-2e440612a39f - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/term v0.20.0 + golang.org/x/term v0.21.0 gopkg.in/gizak/termui.v1 v1.0.0-20151021151108-e62b5929642a gopkg.in/yaml.v3 v3.0.1 ) @@ -57,15 +55,17 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/nats-io/nsc/v2 v2.8.6 // indirect github.com/nsf/termbox-go v1.1.1 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/procfs v0.15.0 // indirect + github.com/prometheus/common v0.54.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.6.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 04c25c5a..605e14bb 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/expr-lang/expr v1.16.7 h1:gCIiHt5ODA0xIaDbD0DPKyZpM9Drph3b3lolYAYq2Kw= -github.com/expr-lang/expr v1.16.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= +github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -73,8 +73,8 @@ github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8S github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -102,14 +102,14 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/nats-io/jsm.go v0.1.1-0.20240517153523-ca97f76bed5c h1:zC8x14tEM2qa5YL5oC/pSUt6hBs7+NCHlmJTm3kD60k= -github.com/nats-io/jsm.go v0.1.1-0.20240517153523-ca97f76bed5c/go.mod h1:VdjGmF69qsEeSIGb1vPqOfwtI8vljZmR+h7sZKD2Rbg= +github.com/nats-io/jsm.go v0.1.1-0.20240621160505-af16666abb2e h1:+boW6xw7KEB7nODgZNN4MhCT0yCzQLL2fTxhAhKojGs= +github.com/nats-io/jsm.go v0.1.1-0.20240621160505-af16666abb2e/go.mod h1:O5DNghYL2JoZFr5BR6pybETzuTAubikW85QzBO1525A= github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c= github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.11.0-preview.2 h1:tT/UeBbFzHRzwy77T/+/Rbw58XP9F3CY3VmtcDltZ68= github.com/nats-io/nats-server/v2 v2.11.0-preview.2/go.mod h1:ILDVzrTqMco4rQMOgEZimBjJHb1oZDlz1J+qhJtZlRM= -github.com/nats-io/nats.go v1.35.0 h1:XFNqNM7v5B+MQMKqVGAyHwYhyKb48jrenXNxIU20ULk= -github.com/nats-io/nats.go v1.35.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU= +github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nsc/v2 v2.8.6 h1:ytf5F2mb+BXx8DjXImPyqOZrFFozt9umQGuQ+6m9eXs= @@ -129,10 +129,10 @@ github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQ github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= -github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= -github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= -github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= +github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= +github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -157,8 +157,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -176,8 +176,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -191,18 +191,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -211,8 +211,8 @@ golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -221,8 +221,8 @@ gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/monitor/monitor.go b/monitor/monitor.go deleted file mode 100644 index 9aeacbf0..00000000 --- a/monitor/monitor.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2023 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "github.com/jedib0t/go-pretty/v6/table" - "github.com/jedib0t/go-pretty/v6/text" -) - -type Status string - -var ( - OKStatus Status = "OK" - WarningStatus Status = "WARNING" - CriticalStatus Status = "CRITICAL" - UnknownStatus Status = "UNKNOWN" -) - -func newTableWriter(title string) table.Writer { - tbl := table.NewWriter() - tbl.SetStyle(table.StyleRounded) - tbl.Style().Title.Align = text.AlignCenter - tbl.Style().Format.Header = text.FormatDefault - - if title != "" { - tbl.SetTitle(title) - } - - return tbl -} diff --git a/monitor/perfdata.go b/monitor/perfdata.go deleted file mode 100644 index a34ec326..00000000 --- a/monitor/perfdata.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2023 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "fmt" - "strings" -) - -type PerfDataItem struct { - Help string `json:"-"` - Name string `json:"name"` - Value float64 `json:"value"` - Warn float64 `json:"warning"` - Crit float64 `json:"critical"` - Unit string `json:"unit,omitempty"` -} - -type PerfData []*PerfDataItem - -func (p PerfData) String() string { - var res []string - for _, i := range p { - res = append(res, i.String()) - } - - return strings.TrimSpace(strings.Join(res, " ")) -} - -func (i *PerfDataItem) String() string { - valueFmt := "%0.0f" - if i.Unit == "s" { - valueFmt = "%0.4f" - } - - pd := fmt.Sprintf("%s="+valueFmt, i.Name, i.Value) - if i.Unit != "" { - pd = pd + i.Unit - } - - if i.Warn > 0 || i.Crit > 0 { - if i.Warn != 0 { - pd = fmt.Sprintf("%s;"+valueFmt, pd, i.Warn) - } else if i.Crit > 0 { - pd = fmt.Sprintf("%s;", pd) - } - - if i.Crit != 0 { - pd = fmt.Sprintf("%s;"+valueFmt, pd, i.Crit) - } - } - - return pd -} diff --git a/monitor/result.go b/monitor/result.go deleted file mode 100644 index 8c45fcbe..00000000 --- a/monitor/result.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2023 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/jedib0t/go-pretty/v6/table" - "github.com/nats-io/natscli/columns" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/expfmt" -) - -type RenderFormat int - -const ( - NagiosFormat RenderFormat = iota - PrometheusFormat - TextFormat - JSONFormat -) - -type Result struct { - Output string `json:"output,omitempty"` - Status Status `json:"status"` - Check string `json:"check_suite"` - Name string `json:"check_name"` - Warnings []string `json:"warning,omitempty"` - Criticals []string `json:"critical,omitempty"` - OKs []string `json:"ok,omitempty"` - PerfData PerfData `json:"perf_data"` - RenderFormat RenderFormat `json:"-"` - NameSpace string `json:"-"` - OutFile string `json:"-"` -} - -func (r *Result) Pd(pd ...*PerfDataItem) { - r.PerfData = append(r.PerfData, pd...) -} - -func (r *Result) CriticalExit(format string, a ...any) { - r.Critical(format, a...) - r.GenericExit() -} - -func (r *Result) Critical(format string, a ...any) { - r.Criticals = append(r.Criticals, fmt.Sprintf(format, a...)) -} - -func (r *Result) Warn(format string, a ...any) { - r.Warnings = append(r.Warnings, fmt.Sprintf(format, a...)) -} - -func (r *Result) Ok(format string, a ...any) { - r.OKs = append(r.OKs, fmt.Sprintf(format, a...)) -} - -func (r *Result) CriticalIfErr(err error, format string, a ...any) bool { - if err == nil { - return false - } - - r.CriticalExit(format, a...) - - return true -} - -func (r *Result) nagiosCode() int { - switch r.Status { - case OKStatus: - return 0 - case WarningStatus: - return 1 - case CriticalStatus: - return 2 - default: - return 3 - } -} - -func (r *Result) exitCode() int { - if r.RenderFormat == PrometheusFormat { - return 0 - } - - return r.nagiosCode() -} - -func (r *Result) Exit() { - os.Exit(r.exitCode()) -} - -func (r *Result) renderHuman() string { - buf := bytes.NewBuffer([]byte{}) - - fmt.Fprintf(buf, "%s: %s\n\n", r.Name, r.Status) - - tblWriter := newTableWriter("") - tblWriter.AppendHeader(table.Row{"Status", "Message"}) - lines := 0 - for _, ok := range r.OKs { - tblWriter.AppendRow(table.Row{"OK", ok}) - lines++ - } - for _, warn := range r.Warnings { - tblWriter.AppendRow(table.Row{"Warning", warn}) - lines++ - } - for _, crit := range r.Criticals { - tblWriter.AppendRow(table.Row{"Critical", crit}) - lines++ - } - - if lines > 0 { - fmt.Fprintln(buf, "Status Detail") - fmt.Fprintln(buf) - fmt.Fprint(buf, tblWriter.Render()) - fmt.Fprintln(buf) - } - - tblWriter = newTableWriter("") - tblWriter.AppendHeader(table.Row{"Metric", "Value", "Unit", "Critical Threshold", "Warning Threshold", "Description"}) - lines = 0 - for _, pd := range r.PerfData { - tblWriter.AppendRow(table.Row{pd.Name, f(pd.Value), pd.Unit, f(pd.Crit), f(pd.Warn), pd.Help}) - lines++ - } - if lines > 0 { - fmt.Fprintln(buf) - fmt.Fprintln(buf, "Check Metrics") - fmt.Fprintln(buf) - fmt.Fprint(buf, tblWriter.Render()) - fmt.Fprintln(buf) - } - - return buf.String() -} - -func (r *Result) renderPrometheus() string { - if r.Check == "" { - r.Check = r.Name - } - - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - prometheus.DefaultGatherer = registry - - sname := strings.ReplaceAll(r.Name, `"`, `.`) - for _, pd := range r.PerfData { - help := fmt.Sprintf("Data about the NATS CLI check %s", r.Check) - if pd.Help != "" { - help = pd.Help - } - - gauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: prometheus.BuildFQName(r.NameSpace, r.Check, pd.Name), - Help: help, - }, []string{"item"}) - prometheus.MustRegister(gauge) - gauge.WithLabelValues(sname).Set(pd.Value) - } - - status := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: prometheus.BuildFQName(r.NameSpace, r.Check, "status_code"), - Help: fmt.Sprintf("Nagios compatible status code for %s", r.Check), - }, []string{"item", "status"}) - prometheus.MustRegister(status) - - status.WithLabelValues(sname, string(r.Status)).Set(float64(r.nagiosCode())) - - var buf bytes.Buffer - - mfs, err := prometheus.DefaultGatherer.Gather() - if err != nil { - panic(err) - } - - for _, mf := range mfs { - _, err = expfmt.MetricFamilyToText(&buf, mf) - if err != nil { - panic(err) - } - } - - return buf.String() -} - -func (r *Result) renderJSON() string { - res, _ := json.MarshalIndent(r, "", " ") - return string(res) -} - -func (r *Result) renderNagios() string { - res := []string{r.Name} - for _, c := range r.Criticals { - res = append(res, fmt.Sprintf("Crit:%s", c)) - } - - for _, w := range r.Warnings { - res = append(res, fmt.Sprintf("Warn:%s", w)) - } - - if r.Output != "" { - res = append(res, r.Output) - } else if len(r.OKs) > 0 { - for _, ok := range r.OKs { - res = append(res, fmt.Sprintf("OK:%s", ok)) - } - } - - if len(r.PerfData) == 0 { - return fmt.Sprintf("%s %s", r.Status, strings.Join(res, " ")) - } - - return fmt.Sprintf("%s %s | %s", r.Status, strings.Join(res, " "), r.PerfData) -} - -func (r *Result) String() string { - if r.Status == "" { - r.Status = UnknownStatus - } - if r.PerfData == nil { - r.PerfData = PerfData{} - } - - switch { - case len(r.Criticals) > 0: - r.Status = CriticalStatus - case len(r.Warnings) > 0: - r.Status = WarningStatus - default: - r.Status = OKStatus - } - - switch r.RenderFormat { - case JSONFormat: - return r.renderJSON() - case PrometheusFormat: - return r.renderPrometheus() - case TextFormat: - return r.renderHuman() - default: - return r.renderNagios() - } -} - -func (r *Result) GenericExit() { - if r.OutFile != "" { - f, err := os.CreateTemp(filepath.Dir(r.OutFile), "") - if err != nil { - fmt.Fprintf(os.Stderr, "temp file failed: %s", err) - os.Exit(1) - } - defer os.Remove(f.Name()) - - _, err = fmt.Fprintln(f, r.String()) - if err != nil { - fmt.Fprintf(os.Stderr, "temp file write failed: %s", err) - os.Exit(1) - } - - err = f.Close() - if err != nil { - fmt.Fprintf(os.Stderr, "temp file write failed: %s", err) - os.Exit(1) - } - - err = os.Chmod(f.Name(), 0600) - if err != nil { - fmt.Fprintf(os.Stderr, "temp file mode change failed: %s", err) - os.Exit(1) - } - - err = os.Rename(f.Name(), r.OutFile) - if err != nil { - fmt.Fprintf(os.Stderr, "temp file rename failed: %s", err) - } - - os.Exit(1) - } - - fmt.Println(r.String()) - - r.Exit() -} - -func f(v any) string { - return columns.F(v) -}