diff --git a/go.mod b/go.mod index 3451fc9..603bbe5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/flashcatcloud/flashduty-cli go 1.25.1 require ( - github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908030757-f478f34797be + github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908113851-5bf8f2902391 github.com/mattn/go-runewidth v0.0.28 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index cd0253b..732a29b 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908025802-4fa9a76d8b57 h1:3 github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908025802-4fa9a76d8b57/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908030757-f478f34797be h1:F3+A0vVRICnEeBshac70P+VBtuo64P5hBmxcfbFxiXk= github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908030757-f478f34797be/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= +github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908113851-5bf8f2902391 h1:u4IM9wE2/isYCAUMszeBsp4b9Na7Qh5mHlvPLpDgvkw= +github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908113851-5bf8f2902391/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-runewidth v0.0.28 h1:rPyg2ybwEKPebvpzVWe1gKBkH8EQFkxO4Y0hjBeLaBU= diff --git a/internal/cli/monit_query.go b/internal/cli/monit_query.go index 3604511..7280e26 100644 --- a/internal/cli/monit_query.go +++ b/internal/cli/monit_query.go @@ -12,76 +12,10 @@ import ( func newMonitQueryCmd() *cobra.Command { cmd := newGroupCmd("monit-query", "Query configured datasources; structured diagnostics use monit datasource-tools-invoke") - cmd.AddCommand(newMonitQueryDiagnoseCmd()) cmd.AddCommand(newMonitQueryDataCmd()) return cmd } -func newMonitQueryDiagnoseCmd() *cobra.Command { - var ( - dsType, dsName, timeStart, timeEnd, inputQuery, operation string - maxLogs, maxPatterns, timeoutSeconds int - ) - - cmd := &cobra.Command{ - Use: "diagnose", - Short: "Legacy log-pattern and metric-trend evidence (prefer monit datasource-tools-invoke)", - Long: curatedLong("Run pre-clustered RCA over a datasource window, returning log_patterns or metric_trends findings.", "Diagnostics", "QueryDiagnose"), - RunE: func(cmd *cobra.Command, args []string) error { - if dsType == "" || dsName == "" || inputQuery == "" { - return fmt.Errorf("--ds-type, --ds-name, --input-query are required") - } - startTime, err := timeutil.Parse(timeStart) - if err != nil { - return fmt.Errorf("invalid --time-start: %w", err) - } - endTime, err := timeutil.Parse(timeEnd) - if err != nil { - return fmt.Errorf("invalid --time-end: %w", err) - } - - return runCommand(cmd, args, func(ctx *RunContext) error { - input := &flashduty.DiagnoseRequest{ - DsType: dsType, - DsName: dsName, - Operation: operation, - Input: flashduty.DiagnoseRequestInput{Query: inputQuery}, - TimeRange: flashduty.DiagnoseRequestTimeRange{Start: startTime, End: endTime}, - } - if maxLogs > 0 { - input.Options.MaxLogsScanned = int64(maxLogs) - } - if maxPatterns > 0 { - input.Options.MaxPatterns = int64(maxPatterns) - } - if timeoutSeconds > 0 { - input.Options.TimeoutSeconds = int64(timeoutSeconds) - } - - //nolint:staticcheck // Keep the legacy command working while callers migrate to datasource tools. - result, _, err := ctx.Client.Diagnostics.QueryDiagnose(cmdContext(ctx.Cmd), input) - if err != nil { - return err - } - return ctx.Printer.Print(result, nil) - }) - }, - } - - cmd.Flags().StringVar(&dsType, "ds-type", "", "Datasource type: loki|victorialogs (log_patterns) or prometheus (metric_trends) (required)") - cmd.Flags().StringVar(&dsName, "ds-name", "", "Datasource name as configured (required)") - registerEnumFlag(cmd, "ds-type", "prometheus", "victorialogs", "loki") - cmd.Flags().StringVar(&timeStart, "time-start", "15m", "Window start: relative duration ('15m'/'1h'), 'now', a date/RFC3339 timestamp, or a unix epoch in seconds or milliseconds") - cmd.Flags().StringVar(&timeEnd, "time-end", "now", "Window end: same formats as --time-start; span capped at 6h") - cmd.Flags().StringVar(&inputQuery, "input-query", "", "Filter-only log query OR matrix PromQL (required)") - cmd.Flags().StringVar(&operation, "operation", "", "log_patterns or metric_trends (default inferred from ds-type)") - cmd.Flags().IntVar(&maxLogs, "max-logs", 0, "Max log lines scanned (default 10000, cap 50000)") - cmd.Flags().IntVar(&maxPatterns, "max-patterns", 0, "Max patterns returned (default 20, cap 50)") - cmd.Flags().IntVar(&timeoutSeconds, "timeout-seconds", 0, "Per-call timeout in seconds (default 25, cap 30)") - - return cmd -} - func newMonitQueryDataCmd() *cobra.Command { var ( dsType, dsName, expr string diff --git a/internal/cli/monit_query_test.go b/internal/cli/monit_query_test.go index 7da20b8..0c43826 100644 --- a/internal/cli/monit_query_test.go +++ b/internal/cli/monit_query_test.go @@ -9,19 +9,6 @@ import ( "time" ) -func TestMonitQueryDiagnoseFlags(t *testing.T) { - cmd := newMonitQueryDiagnoseCmd() - for _, name := range []string{ - "ds-type", "ds-name", "time-start", "time-end", - "input-query", "operation", - "max-logs", "max-patterns", "timeout-seconds", - } { - if cmd.Flags().Lookup(name) == nil { - t.Errorf("flag --%s missing", name) - } - } -} - func TestMonitQueryDataFlags(t *testing.T) { cmd := newMonitQueryDataCmd() for _, name := range []string{"ds-type", "ds-name", "expr", "args", "delay-seconds"} { @@ -31,175 +18,27 @@ func TestMonitQueryDataFlags(t *testing.T) { } } -// --- monit-query diagnose ------------------------------------------------- - -func TestMonitQueryDiagnoseHappyPath(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"operation": "log_patterns"} - - _, err := execCommand( - "monit-query", "diagnose", - "--ds-type", "victorialogs", - "--ds-name", "vl-prod", - "--input-query", `{app="api"}`, - "--operation", "log_patterns", - "--max-logs", "5000", - "--max-patterns", "10", - "--timeout-seconds", "20", - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if stub.lastPath != "/monit/query/diagnose" { - t.Fatalf("expected /monit/query/diagnose, got %q", stub.lastPath) - } - body := stub.lastBody - if body["ds_type"] != "victorialogs" || body["ds_name"] != "vl-prod" { - t.Errorf("unexpected ds fields: %#v", body) - } - input, _ := body["input"].(map[string]any) - if input["query"] != `{app="api"}` { - t.Errorf("expected input query %q, got %v", `{app="api"}`, input["query"]) - } - if body["operation"] != "log_patterns" { - t.Errorf("expected operation log_patterns, got %v", body["operation"]) - } - options, _ := body["options"].(map[string]any) - if fmt.Sprint(options["max_logs_scanned"]) != "5000" || - fmt.Sprint(options["max_patterns"]) != "10" || - fmt.Sprint(options["timeout_seconds"]) != "20" { - t.Errorf("unexpected caps: %#v", options) - } - timeRange, _ := body["time_range"].(map[string]any) - if fmt.Sprint(timeRange["start"]) == "0" || fmt.Sprint(timeRange["start"]) == "" || - fmt.Sprint(timeRange["end"]) == "0" || fmt.Sprint(timeRange["end"]) == "" { - t.Errorf("expected non-zero default time range, got %#v", timeRange) - } -} - -func TestMonitQueryDiagnoseRendersMetricEvidence(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{ - "schema_version": "2", - "operation": "metric_trends", - "ds_type": "prometheus", - "ds_name": "prod-prometheus", - "query": "up", - "window": map[string]any{"start": "2026-07-14T06:00:00Z", "end": "2026-07-14T07:00:00Z"}, - "results": []any{map[string]any{ - "method": "window_compare", - "window": map[string]any{"start": "2026-07-14T06:00:00Z", "end": "2026-07-14T07:00:00Z"}, - "summary": map[string]any{ - "series_total": 1, "series_analyzed": 1, "selected_series_total": 1, "series_returned": 1, - "analysis_truncated": false, "evidence_summary": "One series changed.", - }, - "series_evidence": []any{map[string]any{ - "labels": map[string]any{"instance": "api-1"}, - "observations": []any{"The current average increased."}, - }}, - "warnings": []any{}, - }}, - } - - out, err := execCommand( - "monit-query", "diagnose", - "--ds-type", "prometheus", - "--ds-name", "prod-prometheus", - "--input-query", "up", - "--operation", "metric_trends", - "--output-format", "json", - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - var rendered map[string]any - if err := json.Unmarshal([]byte(out), &rendered); err != nil { - t.Fatalf("decode CLI JSON: %v\n%s", err, out) - } - if _, found := rendered["data_handling"]; found { - t.Fatalf("metric output fabricated data_handling: %s", out) - } - evidence := rendered["results"].([]any)[0].(map[string]any)["series_evidence"].([]any)[0].(map[string]any) - for _, field := range []string{"comparison_status", "current_window_stats", "baseline_window_stats"} { - if _, found := evidence[field]; found { - t.Fatalf("metric evidence fabricated %s: %s", field, out) - } - } -} - -func TestMonitQueryDiagnoseRequiredFlags(t *testing.T) { - cases := []struct { - name string - args []string - }{ - { - name: "missing ds-type", - args: []string{ - "monit-query", "diagnose", - "--ds-name", "vl-prod", - "--input-query", `{app="api"}`, - }, - }, - { - name: "missing ds-name", - args: []string{ - "monit-query", "diagnose", - "--ds-type", "victorialogs", - "--input-query", `{app="api"}`, - }, - }, - { - name: "missing input-query", - args: []string{ - "monit-query", "diagnose", - "--ds-type", "victorialogs", - "--ds-name", "vl-prod", - }, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { +func TestRetiredMonitCommandsRejectBeforeRequest(t *testing.T) { + for _, args := range [][]string{ + {"monit-query", "diagnose"}, {"monit", "query-diagnose"}, + {"monit", "rule-counter-status"}, + {"monit", "store-ruleset-create"}, {"monit", "store-ruleset-update"}, + {"monit", "store-ruleset-list"}, {"monit", "store-ruleset-info"}, {"monit", "store-ruleset-delete"}, + } { + t.Run(strings.Join(args, " "), func(t *testing.T) { saveAndResetGlobals(t) stub := newGFStub(t) - - _, err := execCommand(tc.args...) - if err == nil { - t.Fatal("expected required-flag error, got nil") - } - if !strings.Contains(err.Error(), "required") { - t.Errorf("expected error to mention 'required', got %q", err.Error()) + _, err := execCommand(args...) + if err == nil || !strings.Contains(err.Error(), "unknown command") { + t.Fatalf("retired command error=%v", err) } if stub.requests != 0 { - t.Errorf("diagnose should not have been called: %d request(s)", stub.requests) + t.Fatalf("retired command sent %d requests", stub.requests) } }) } } -func TestMonitQueryDiagnoseInvalidTimeStart(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - - _, err := execCommand( - "monit-query", "diagnose", - "--ds-type", "victorialogs", - "--ds-name", "vl-prod", - "--input-query", `{app="api"}`, - "--time-start", "not-a-time", - ) - if err == nil { - t.Fatal("expected error for invalid --time-start, got nil") - } - if !strings.Contains(err.Error(), "--time-start") { - t.Errorf("expected error to mention --time-start, got %q", err.Error()) - } - if stub.requests != 0 { - t.Errorf("diagnose should not have been called: %d request(s)", stub.requests) - } -} - // --- monit-query data ----------------------------------------------------- func TestMonitQueryDataHappyPath(t *testing.T) { diff --git a/internal/cli/zz_generated_alert_rules.go b/internal/cli/zz_generated_alert_rules.go index 2cdae2a..a5f494b 100644 --- a/internal/cli/zz_generated_alert_rules.go +++ b/internal/cli/zz_generated_alert_rules.go @@ -182,45 +182,6 @@ API: POST /monit/rule/counter/node (monit-rule-read-counter-node) return cmd } -func genAlertRulesReadCounterStatusCmd() *cobra.Command { - var dataJSON string - cmd := &cobra.Command{ - Use: "rule-counter-status", - Short: "Get rule status counters for top-level folders", - Long: `Get rule status counters for top-level folders. - -Return trigger status summary for all top-level folder nodes — used for the overview dashboard. - -API: POST /monit/rule/counter/status (monit-rule-read-counter-status) - -Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): - - folder_id (integer) (required) — ID of the folder (grouping node). - - folder_name (string) — Folder name; omitted by some endpoints ('omitempty'). - - rule_total (integer) (required) — Total rules in the folder family. - - triggered_rule_count (integer) (required) — Rules with active alerts. -`, - Example: ` flashduty monit rule-counter-status --data '{}'`, - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) error { - return nil - }) - if err != nil { - return err - } - _ = body - out, _, err := ctx.Client.AlertRules.ReadCounterStatus(cmdContext(ctx.Cmd)) - if err != nil { - return err - } - return printGenericResult(ctx, out) - }) - }, - } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") - return cmd -} - func genAlertRulesReadCounterTotalCmd() *cobra.Command { var dataJSON string cmd := &cobra.Command{ @@ -1566,7 +1527,6 @@ func registerGeneratedAlertRules(root *cobra.Command) { genAddLeaf(gMonit, genAlertRulesReadAuditsCmd()) genAddLeaf(gMonit, genAlertRulesReadCounterChannelCmd()) genAddLeaf(gMonit, genAlertRulesReadCounterNodeCmd()) - genAddLeaf(gMonit, genAlertRulesReadCounterStatusCmd()) genAddLeaf(gMonit, genAlertRulesReadCounterTotalCmd()) genAddLeaf(gMonit, genAlertRulesReadDstypesCmd()) genAddLeaf(gMonit, genAlertRulesReadExportCmd()) diff --git a/internal/cli/zz_generated_data_sources.go b/internal/cli/zz_generated_data_sources.go index 546fd4f..29acd45 100644 --- a/internal/cli/zz_generated_data_sources.go +++ b/internal/cli/zz_generated_data_sources.go @@ -354,7 +354,7 @@ Request fields: --edge-cluster-name string (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. --enabled bool — Whether business execution is enabled. Omitted on create: true; omitted on update: preserve the current value. Explicit false disables execution; null is invalid. Does not change alerting_enabled. --id int — Datasource ID. Required for update; omit for create. - --name string (required) — Datasource display name. This is the name referenced as 'ds_name' in query and diagnose APIs. + --name string (required) — Datasource display name. This is the name referenced as 'ds_name' in query APIs. --note string — Optional description. --type-ident string (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'tencent_cls', 'victorialogs', 'redis_node', 'redis_sentinel', 'mongodb_mongod', 'mongodb_mongos', 'kafka'。 payload (object, via --data) (required) — Type-specific configuration block. Must include the key matching 'type_ident'. For diagnostic types, password and Kafka tls_key are omitted from responses unless they are ${env:...} references. On update, omit those fields to preserve stored secrets; explicitly send an empty string to clear. Other configuration fields retain their existing behavior. @@ -581,7 +581,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fEdgeClusterName, "edge-cluster-name", "", "Monitors edge cluster name responsible for evaluating rules using this datasource. (required)") cmd.Flags().BoolVar(&fEnabled, "enabled", false, "Whether business execution is enabled. Omitted on create: true; omitted on update: preserve the current value. Explicit false disables execution; null is invalid. Does not change alerting_enabled.") cmd.Flags().Int64Var(&fID, "id", 0, "Datasource ID. Required for update; omit for create.") - cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. This is the name referenced as 'ds_name' in query and diagnose APIs. (required)") + cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. This is the name referenced as 'ds_name' in query APIs. (required)") cmd.Flags().StringVar(&fNote, "note", "", "Optional description.") cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'tencent_cls', 'victorialogs', 'redis_node', 'redis_sentinel', 'mongodb_mongod', 'mongodb_mongos', 'kafka'。 (required)") cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") @@ -661,7 +661,7 @@ Request fields: --edge-cluster-name string (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. --enabled bool — Whether business execution is enabled. Omitted on create: true; omitted on update: preserve the current value. Explicit false disables execution; null is invalid. Does not change alerting_enabled. --id int — Datasource ID. Required for update; omit for create. - --name string (required) — Datasource display name. This is the name referenced as 'ds_name' in query and diagnose APIs. + --name string (required) — Datasource display name. This is the name referenced as 'ds_name' in query APIs. --note string — Optional description. --type-ident string (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'tencent_cls', 'victorialogs', 'redis_node', 'redis_sentinel', 'mongodb_mongod', 'mongodb_mongos', 'kafka'。 payload (object, via --data) (required) — Type-specific configuration block. Must include the key matching 'type_ident'. For diagnostic types, password and Kafka tls_key are omitted from responses unless they are ${env:...} references. On update, omit those fields to preserve stored secrets; explicitly send an empty string to clear. Other configuration fields retain their existing behavior. @@ -888,7 +888,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fEdgeClusterName, "edge-cluster-name", "", "Monitors edge cluster name responsible for evaluating rules using this datasource. (required)") cmd.Flags().BoolVar(&fEnabled, "enabled", false, "Whether business execution is enabled. Omitted on create: true; omitted on update: preserve the current value. Explicit false disables execution; null is invalid. Does not change alerting_enabled.") cmd.Flags().Int64Var(&fID, "id", 0, "Datasource ID. Required for update; omit for create.") - cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. This is the name referenced as 'ds_name' in query and diagnose APIs. (required)") + cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. This is the name referenced as 'ds_name' in query APIs. (required)") cmd.Flags().StringVar(&fNote, "note", "", "Optional description.") cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'tencent_cls', 'victorialogs', 'redis_node', 'redis_sentinel', 'mongodb_mongod', 'mongodb_mongos', 'kafka'。 (required)") cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") diff --git a/internal/cli/zz_generated_diagnostics.go b/internal/cli/zz_generated_diagnostics.go index 9d01aa0..8811e40 100644 --- a/internal/cli/zz_generated_diagnostics.go +++ b/internal/cli/zz_generated_diagnostics.go @@ -93,180 +93,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le return cmd } -func genDiagnosticsQueryDiagnoseCmd() *cobra.Command { - var dataJSON string - var fAccountID int64 - var fDsName string - var fDsType string - var fOperation string - cmd := &cobra.Command{ - Use: "query-diagnose", - Short: "Diagnose data source", - Deprecated: "this API operation is deprecated", - Long: `Diagnose data source. - -Run a synchronous diagnostic query ('log_patterns' for Loki/VictoriaLogs, 'metric_trends' for Prometheus). Used by Flashduty AI SRE for log-pattern clustering and time-series trend analysis. Long-running — up to 35 s. - -Deprecated: migrate to /monit/datasource/tools/invoke with prometheus.metric_trends, loki.log_patterns or victorialogs.log_patterns. Retained for existing consumers; the legacy request and response remain unchanged. - -API: POST /monit/query/diagnose (monit-read-query-diagnose) - -Request fields: - --account-id int — Optional consistency check. Must equal the authenticated account when supplied. - --ds-name string (required) — Data source name configured under the tenant. - --ds-type string (required) — Data source type. 'log_patterns' supports 'loki' and 'victorialogs'; 'metric_trends' supports 'prometheus'. - --operation string — Diagnostic operation. When omitted, inferred from 'ds_type' (loki / victorialogs → 'log_patterns', prometheus → 'metric_trends'). Other sources must specify explicitly. [log_patterns, metric_trends] - input (object, via --data) (required) — Diagnose input. 'query' is required: LogQL / VictoriaLogs query syntax for 'log_patterns'; PromQL for 'metric_trends'. - - query (string) (required) — Query expression. LogQL / VictoriaLogs query syntax for 'log_patterns'; PromQL for 'metric_trends'. - methods (array, via --data) — Diagnostic methods to run. When omitted, 'log_patterns' defaults to 'pattern_snapshot + pattern_compare(previous_window)' and 'metric_trends' defaults to 'single_window_shape + window_compare(previous_window)'. - - baseline (string) — Only meaningful for compare-style methods. Defaults to 'previous_window'. 'previous_window' = the equal-length window immediately before the current window; 'same_window_yesterday' = the current window shifted back 24 hours; 'same_window_last_week' = the current window shifted back 7 days. [previous_window, same_window_yesterday, same_window_last_week] - - name (string) — 'log_patterns' supports 'pattern_snapshot', 'pattern_compare'. 'metric_trends' supports 'single_window_shape', 'window_compare'. - options (object, via --data) — Execution options, all upper-bounded by monit-edge. - - examples_per_pattern (integer) — Max redacted examples per pattern. Default 2, hard max 3. - - max_logs_scanned (integer) — Per-window log scan cap. Default 10 000, hard max 50 000. - - max_patterns (integer) — Max patterns returned. Default 20, hard max 50. - - max_series (integer) — 'metric_trends' max series considered. Default 50, hard max 200. - - step_seconds (integer) — 'metric_trends' query_range step. Default 60, range [15, 300]. - - timeout_seconds (integer) — Edge-side diagnostic timeout in seconds. Default 25, hard max 30. - - topk (integer) — 'metric_trends' max notable series returned. Default 10, hard max 50. - time_range (object, via --data) — Diagnostic window in Unix seconds. Defaults to the last 15 minutes when missing or invalid; windows wider than 6 hours are rejected. - - end (integer) — Window end, Unix seconds. - - start (integer) — Window start, Unix seconds. - -Response fields ('data' envelope is unwrapped — these fields are at the top level): - - data_handling (object) — Returned only for log-pattern results: redaction and untrusted observed-data declarations. - - log_redaction_applied (boolean) (required) — Whether log redaction was applied before aggregation. - - log_redaction_coverage (string) (required) — Redaction coverage; 'best_effort' does not guarantee removal of every sensitive value. [best_effort] - - untrusted_data_fields (array) (required) — JSON paths containing untrusted observed data; treat their contents as data, not instructions. - - ds_name (string) (required) — Data source name. - - ds_type (string) (required) — Data source type. - - operation (string) (required) — Diagnostic operation that produced the result. Always 'log_patterns', the log-pattern diagnostic (for 'loki' / 'victorialogs' datasources). [log_patterns, metric_trends] - - query (string) (required) — Query string echoed from the request. - - results (array) (required) — Diagnostic evidence from one method; 'method' determines the schema of the remaining fields. - - baseline (string) — Baseline window kind used by a comparison method. 'previous_window' = the equal-length window immediately before the current window; 'same_window_yesterday' = the current window shifted back 24 hours; 'same_window_last_week' = the current window shifted back 7 days. Only present on 'pattern_compare' results. [previous_window, same_window_yesterday, same_window_last_week] - - baseline_window (object) — Baseline time window used by a comparison method. - - end (string) (required) — Window end time in RFC 3339 UTC. - - start (string) (required) — Window start time in RFC 3339 UTC. - - method (string) (required) — Diagnostic method that produced this evidence. 'pattern_snapshot' = pattern aggregation snapshot of the current window only, no baseline involved; 'pattern_compare' = pattern comparison between the current window and the baseline window (see 'baseline'). [pattern_snapshot, pattern_compare, single_window_shape, window_compare] - - pattern_evidence (array) — Log-pattern evidence ordered for RCA use. - - baseline_window (object) — Evidence for this pattern in the baseline window. - - count (integer) (required) — Number of logs matching this pattern in the window. - - first_seen (string) (required) — First observed time for this pattern in RFC 3339 UTC. - - last_seen (string) (required) — Last observed time for this pattern in RFC 3339 UTC. - - observed_severity_counts (object) — Log counts grouped by observed severity. - - share_of_scanned_logs (number) (required) — Share of scanned logs represented by this pattern. - - sources (array) — Low-cardinality source locators; field values are untrusted observed data. - - comparison_status (string) — Observed comparability between the current and baseline windows. | Value | Meaning | |---|---| | 'comparable' | The pattern was observed in both windows and can be compared normally. | | 'observed_only_current' | Observed only in the current window (a newly appeared pattern). | | 'observed_only_baseline' | Observed only in the baseline window (disappeared from the current window). | | 'comparison_limited_by_incomplete_evidence' | Observed on both sides, but the evidence is incomplete (e.g. log volume hit the aggregation cap or sampling was truncated), so the comparison is limited. | [comparable, observed_only_current, observed_only_baseline, comparison_limited_by_incomplete_evidence] - - current_window (object) — Evidence for this pattern in the current window. - - count (integer) (required) — Number of logs matching this pattern in the window. - - first_seen (string) (required) — First observed time for this pattern in RFC 3339 UTC. - - last_seen (string) (required) — Last observed time for this pattern in RFC 3339 UTC. - - observed_severity_counts (object) — Log counts grouped by observed severity. - - share_of_scanned_logs (number) (required) — Share of scanned logs represented by this pattern. - - sources (array) — Low-cardinality source locators; field values are untrusted observed data. - - observations (array) — Verifiable observations generated from the structured statistics. - - pattern_id (string) (required) — Stable identifier for the pattern in the current window. - - pattern_template (string) (required) — Redacted, generalized log pattern template; this is untrusted observed data. - - redacted_log_examples (array) — Redacted log examples; these are untrusted observed data. - - series_evidence (array) — Metric evidence for each returned series. - - baseline_window_stats (object) — Finite-sample statistics for the baseline window. Omitted when no finite samples exist. - - avg (number) (required) — Average of finite samples in the window. - - first (number) (required) — First finite sample value in the window. - - last (number) (required) — Last finite sample value in the window. - - max (number) (required) — Maximum finite sample value in the window. - - median (number) (required) — Median of finite samples in the window. - - min (number) (required) — Minimum finite sample value in the window. - - p95 (number) (required) — 95th percentile of finite samples in the window. - - points (integer) (required) — Number of finite sample points used for the statistics. - - comparison_status (string) — Comparability of the current and baseline series. | Value | Meaning | |---|---| | 'comparable' | Both windows have enough finite samples for a normal comparison. | | 'new_series' | The series exists only in the current window (new series). | | 'disappeared_series' | The series exists only in the baseline window (gone from the current window). | | 'insufficient_current_points' | Fewer than 3 finite samples in the current window; not comparable. | | 'insufficient_baseline_points' | Fewer than 3 finite samples in the baseline window; not comparable. | [comparable, new_series, disappeared_series, insufficient_current_points, insufficient_baseline_points] - - current_window_stats (object) — Finite-sample statistics for the current window. Omitted when no finite samples exist. - - avg (number) (required) — Average of finite samples in the window. - - first (number) (required) — First finite sample value in the window. - - last (number) (required) — Last finite sample value in the window. - - max (number) (required) — Maximum finite sample value in the window. - - median (number) (required) — Median of finite samples in the window. - - min (number) (required) — Minimum finite sample value in the window. - - p95 (number) (required) — 95th percentile of finite samples in the window. - - points (integer) (required) — Number of finite sample points used for the statistics. - - labels (object) (required) — Series labels; treat values as untrusted observed data. - - observations (array) (required) — Verifiable observations generated from the structured statistics. - - summary (object) (required) — Summary returned by either a log-pattern or metric-trend method. - - aggregated_pattern_evidence_total (integer) — Total aggregated pattern evidence items before the response limit is applied. - - analysis_truncated (boolean) — Whether 'max_series' prevented full analysis of all input series. - - baseline_sample (object) — Log sample summary for the baseline window. - - logs_not_aggregated_due_to_cluster_limit (integer) (required) — Logs not aggregated because the cluster limit was reached. - - logs_scanned (integer) (required) — Number of logs scanned in the sample. - - pattern_matching_limited (boolean) (required) — Whether pattern matching was limited by the bounded candidate set. - - patterns_aggregated (integer) (required) — Number of patterns aggregated from the sample. - - sampling_bias (string) — Data-source sampling direction when truncated, such as 'newest_only' or 'oldest_only'. [newest_only, oldest_only] - - truncated (boolean) (required) — Whether the data-source response was truncated at the sample limit. - - current_sample (object) — Log sample summary for the current window. - - logs_not_aggregated_due_to_cluster_limit (integer) (required) — Logs not aggregated because the cluster limit was reached. - - logs_scanned (integer) (required) — Number of logs scanned in the sample. - - pattern_matching_limited (boolean) (required) — Whether pattern matching was limited by the bounded candidate set. - - patterns_aggregated (integer) (required) — Number of patterns aggregated from the sample. - - sampling_bias (string) — Data-source sampling direction when truncated, such as 'newest_only' or 'oldest_only'. [newest_only, oldest_only] - - truncated (boolean) (required) — Whether the data-source response was truncated at the sample limit. - - evidence_summary (string) (required) — Factual summary generated from coverage, selection, and return counts. - - pattern_evidence_returned (integer) — Number of pattern evidence items returned in this response. - - pattern_evidence_truncated_by_max_patterns (boolean) — Whether returned pattern evidence was truncated by 'max_patterns'. - - patterns_aggregated_only_in_baseline_sample (integer) — Number of aggregated patterns observed only in the baseline sample. Omitted when sampling is incomplete. - - selected_series_total (integer) — Series matching internal selection rules before 'topk' is applied. - - series_analyzed (integer) — Number of series analyzed after applying 'max_series'. - - series_returned (integer) — Number of 'series_evidence' items returned in this response. - - series_total (integer) — Total input series; for comparisons, the union of current and baseline label sets. - - warnings (array) (required) — Non-fatal warnings produced during analysis. - - window (object) (required) — Current analysis window using RFC 3339 UTC timestamps. - - end (string) (required) — Window end time in RFC 3339 UTC. - - start (string) (required) — Window start time in RFC 3339 UTC. - - schema_version (string) (required) — Schema version of the edge diagnostic result. Fixed at '2', identifying the response-structure version; bumped on incompatible structural changes. [2] - - window (object) (required) — Current analysis window using RFC 3339 UTC timestamps. - - end (string) (required) — Window end time in RFC 3339 UTC. - - start (string) (required) — Window start time in RFC 3339 UTC. -`, - Example: ` flashduty monit query-diagnose --data '{"account_id":10001,"ds_name":"vmlogs-read","ds_type":"victorialogs","input":{"query":"_stream:{status='\''500'\''}"},"methods":[{"name":"pattern_snapshot"},{"baseline":"same_window_yesterday","name":"pattern_compare"}],"operation":"log_patterns","options":{"examples_per_pattern":2,"max_logs_scanned":10000,"max_patterns":20,"timeout_seconds":25},"time_range":{"end":1776849344,"start":1776847544}}'`, - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) error { - if cmd.Flags().Changed("account-id") { - body["account_id"] = fAccountID - } - if cmd.Flags().Changed("ds-name") { - body["ds_name"] = fDsName - } - if cmd.Flags().Changed("ds-type") { - body["ds_type"] = fDsType - } - if cmd.Flags().Changed("operation") { - body["operation"] = fOperation - } - return nil - }) - if err != nil { - return err - } - req := new(flashduty.DiagnoseRequest) - if err := genBindBody(body, req); err != nil { - return err - } - out, _, err := ctx.Client.Diagnostics.QueryDiagnose(cmdContext(ctx.Cmd), req) - if err != nil { - return err - } - return printGenericResult(ctx, out) - }) - }, - } - cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied.") - cmd.Flags().StringVar(&fDsName, "ds-name", "", "Data source name configured under the tenant. (required)") - cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type. 'log_patterns' supports 'loki' and 'victorialogs'; 'metric_trends' supports 'prometheus'. (required)") - cmd.Flags().StringVar(&fOperation, "operation", "", "Diagnostic operation. When omitted, inferred from 'ds_type' (loki / victorialogs → 'log_patterns', prometheus → 'metric_trends'). Other sources must specify explicitly. [log_patterns, metric_trends]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") - return cmd -} - func registerGeneratedDiagnostics(root *cobra.Command) { gMonit := genGroup(root, "monit", "Monitors API") genAddLeaf(gMonit, genDiagnosticsQueryDataCmd()) - genAddLeaf(gMonit, genDiagnosticsQueryDiagnoseCmd()) } diff --git a/internal/cli/zz_generated_manifest.go b/internal/cli/zz_generated_manifest.go index d56264a..9a94201 100644 --- a/internal/cli/zz_generated_manifest.go +++ b/internal/cli/zz_generated_manifest.go @@ -193,12 +193,10 @@ var generatedOpIDs = []string{ "monit-datasource-write-delete", "monit-datasource-write-update", "monit-read-query-data", - "monit-read-query-diagnose", "monit-rule-read-audit-detail", "monit-rule-read-audits", "monit-rule-read-counter-channel", "monit-rule-read-counter-node", - "monit-rule-read-counter-status", "monit-rule-read-counter-total", "monit-rule-read-dstypes", "monit-rule-read-export", @@ -211,11 +209,6 @@ var generatedOpIDs = []string{ "monit-rule-write-import", "monit-rule-write-move", "monit-rule-write-update", - "monit-store-ruleset-create", - "monit-store-ruleset-delete", - "monit-store-ruleset-info", - "monit-store-ruleset-list", - "monit-store-ruleset-update", "oncall-license-read-license-list", "personInfos", "postmortem-read-list-templates", diff --git a/internal/cli/zz_generated_register.go b/internal/cli/zz_generated_register.go index 3a3e9a2..001feab 100644 --- a/internal/cli/zz_generated_register.go +++ b/internal/cli/zz_generated_register.go @@ -17,7 +17,6 @@ func registerGenerated(root *cobra.Command) { registerGeneratedAlertRules(root) registerGeneratedDataSources(root) registerGeneratedDiagnostics(root) - registerGeneratedRuleSets(root) registerGeneratedAlertEnrichment(root) registerGeneratedAlerts(root) registerGeneratedAnalytics(root) diff --git a/internal/cli/zz_generated_response_help.go b/internal/cli/zz_generated_response_help.go index 099cf8a..c1c94ac 100644 --- a/internal/cli/zz_generated_response_help.go +++ b/internal/cli/zz_generated_response_help.go @@ -25,7 +25,6 @@ var responseHelpBySDKMethod = map[string]string{ "AlertEnrichment.MappingSchemaWriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - schema_id (string) (required) — Created schema ID (MongoDB ObjectID hex).\n - schema_name (string) (required) — Schema name.\n", "AlertRules.ReadAuditDetail": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — ID of the account that owns the rule.\n - action (string) (required) — Action performed: `create` = rule created; `update` = rule updated (covers full updates, field-batch updates, imports and moves). [create, update]\n - alert_rule_id (integer) (required) — ID of the alert rule this record belongs to.\n - content (string) — JSON string of the full rule snapshot at audit time. Populated on `/monit/rule/audit/detail`, omitted on list responses.\n - created_at (string) (required) — When this audit record was produced, as a Unix timestamp in seconds; equals the rule's `updated_at` at change time. CLI `--json` renders this as an RFC3339 string in the process's local timezone (NOT UTC, and NOT the wire integer); an unset value renders as null.\n - creator_id (integer) (required) — ID of the user who made this change (taken from the rule's `updater_id` at change time).\n - creator_name (string) (required) — Name of the user who made this change (taken from the rule's `updater_name` at change time).\n - id (integer) (required) — Audit record ID.\n", "AlertRules.ReadAudits": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — ID of the account that owns the rule.\n - action (string) (required) — Action performed: `create` = rule created; `update` = rule updated (covers full updates, field-batch updates, imports and moves). [create, update]\n - alert_rule_id (integer) (required) — ID of the alert rule this record belongs to.\n - content (string) — JSON string of the full rule snapshot at audit time. Populated on `/monit/rule/audit/detail`, omitted on list responses.\n - created_at (string) (required) — When this audit record was produced, as a Unix timestamp in seconds; equals the rule's `updated_at` at change time. CLI `--json` renders this as an RFC3339 string in the process's local timezone (NOT UTC, and NOT the wire integer); an unset value renders as null.\n - creator_id (integer) (required) — ID of the user who made this change (taken from the rule's `updater_id` at change time).\n - creator_name (string) (required) — Name of the user who made this change (taken from the rule's `updater_name` at change time).\n - id (integer) (required) — Audit record ID.\n", - "AlertRules.ReadCounterStatus": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - folder_id (integer) (required) — ID of the folder (grouping node).\n - folder_name (string) — Folder name; omitted by some endpoints (`omitempty`).\n - rule_total (integer) (required) — Total rules in the folder family.\n - triggered_rule_count (integer) (required) — Rules with active alerts.\n", "AlertRules.ReadCounterTotal": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — ID of the account this snapshot belongs to.\n - clock (string) (required) — Sample timestamp, Unix epoch seconds. CLI `--json` renders this as an RFC3339 string in the process's local timezone (NOT UTC, and NOT the wire integer); an unset value renders as null.\n - id (integer) (required) — ID of this snapshot record.\n - num (integer) (required) — Rule count at the sample time.\n", "AlertRules.ReadDstypes": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Owning account ID. `0` for global types.\n - id (integer) (required) — ID of the datasource type record.\n - ident (string) (required) — Identifier used as the `ds_type` of rules, e.g. `prometheus`.\n - name (string) (required) — Display name, e.g. `Prometheus`.\n - weight (integer) (required) — Display order weight; higher appears first.\n", "AlertRules.ReadExport": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - annotations (object) — Custom annotation key-value pairs attached to alert events; keys must not start with `$` (reserved for query field references).\n - cron_pattern (string) (required) — Evaluation schedule as a 6-field cron expression (seconds included) or `@every ` (an integral number of seconds, at least 1s); `CRON_TZ=`/`TZ=` prefixes are rejected — set the timezone in `timezone` instead.\n - debug_log_enabled (boolean) (required) — Whether to emit debug logs for this rule's evaluations; enable when troubleshooting.\n - delay_seconds (integer) — Query time offset in seconds: each evaluation reads data as of `schedule time − delay_seconds` to tolerate ingestion lag; `0` means no offset.\n - description (string) — Rule description in the format given by `description_type`, shown with alert events.\n - description_type (string) — Format of `description`, `text` or `markdown`; treated as `text` when omitted. [text, markdown]\n - ds_ids (array) — Datasource ID list, merged with `ds_list`; references by ID and is therefore immune to datasource renames.\n - ds_list (array) — Datasource name list with wildcard support; merged with `ds_ids` to decide which datasources the rule monitors — must be maintained by hand if a datasource is renamed.\n - ds_type (string) (required) — Datasource type ident, e.g. `prometheus`; must be a datasource type (`ident`) that exists in the import target environment.\n - enabled (boolean) (required) — Whether the rule is enabled; rules imported as disabled are not evaluated.\n - enabled_times (array) — Effective time windows; each entry has `days` (0–6, 0 = Sunday) and `stime`/`etime` (`HH:MM`), interpreted in the rule's `timezone`; an empty list disables the rule.\n - days (array) — Days of week, 0 = Sunday.\n - etime (string) — End time, e.g. `18:00`.\n - stime (string) — Start time, e.g. `09:00`.\n - labels (object) — Custom label key-value pairs attached to alert events produced by this rule.\n - name (string) (required) — Rule name, up to 128 characters when imported.\n - repeat_interval (integer) — Interval in seconds between repeated notifications for a firing alert; values below 1 fall back to the default of 3600.\n - repeat_total (integer) — Maximum number of repeated notifications for the same alert; values below 1 fall back to the default of 3.\n - rule_configs (object) — Rule evaluation configuration.\n - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows.\n - alerting_check_times (integer) — Number of consecutive evaluations that must satisfy the condition before alerting; minimum 1.\n - enabled (boolean) — Whether any-data checking is enabled: any returned data row triggers an alert.\n - push_recovery_event (boolean) — Whether to push a recovery event notification when the alert resolves.\n - recovery (object) — Recovery condition for any-data check. If omitted or `mode` is empty, treated as `nodata`.\n - args (object) — Datasource-specific options for the recovery query, same convention as `queries[].args`; required for Elasticsearch datasources when `mode` is `ql`.\n - condition (string) — Recovery expression. Required when `mode` is `ql`.\n - mode (string) — `nodata` = recover when the query returns no data; `ql` = recover when the `condition` expression evaluates to true. When `mode` is `ql`, only a single query (`name=A`) is permitted. [nodata, ql]\n - recovery_check_times (integer) — Number of consecutive evaluations that must satisfy the recovery condition before resolving; minimum 1.\n - severity (string) — Severity of any-data alert events; case-sensitive. [Critical, Warning, Info]\n - check_nodata (object) — No-data check configuration.\n - alert_on_empty_result (boolean) — Whether to trigger an alert when every query returns an empty result.\n - alert_on_empty_result_severity (string) — Severity of empty-result alerts, case-sensitive; only effective when `alert_on_empty_result` is enabled. [Critical, Warning, Info]\n - alerting_check_times (integer) — Number of consecutive evaluations that must satisfy the condition before alerting; minimum 1.\n - enabled (boolean) — Whether no-data checking is enabled: a previously-seen series that stops returning data triggers an alert.\n - push_recovery_event (boolean) — Whether to push a recovery event notification when the alert resolves.\n - recovery_check_times (integer) — Number of consecutive evaluations that must satisfy the recovery condition before resolving; minimum 1.\n - resolve_timeout (integer) — Auto-resolve after N seconds.\n - severity (string) — Severity of no-data alert events; case-sensitive. [Critical, Warning, Info]\n - check_threshold (object) — Threshold check configuration.\n - alerting_check_times (integer) — Number of consecutive evaluations that must satisfy the condition before alerting; minimum 1.\n - critical (string) — Critical threshold expression referencing query results via `$` or `$.`, e.g. `$A > 90`; at least one severity must be configured.\n - enabled (boolean) — Whether threshold checking is enabled.\n - info (string) — Info threshold expression, same syntax as `critical`.\n - push_recovery_event (boolean) — Whether to push a recovery event notification when the alert resolves.\n - recovery (object) — Recovery evaluation configuration for threshold checks.\n - args (object) — Datasource-specific extra parameters for the recovery query, using the same `.` key convention as query `args`. Omitted when empty.\n - condition (string) — Recovery condition expression; required when `mode` is `threshold` or `ql`, and must be empty for `invert`.\n - mode (string) — Recovery mode: `invert` = resolve when the alert expression no longer holds (`condition` stays empty); `threshold` = resolve when the `condition` threshold expression holds; `ql` = resolve when the `condition` query expression evaluates true. [invert, threshold, ql]\n - value_fields (array) — Numeric result fields the recovery `condition` references as `$A.`; same semantics as the query's `value_fields`. Omitted when empty.\n - recovery_check_times (integer) — Number of consecutive evaluations that must satisfy the recovery condition before resolving; minimum 1.\n - warning (string) — Warning threshold expression, same syntax as `critical`.\n - queries (array) (required) — Query list with at least one entry; each needs a unique `name` (`R` and `__all__` are reserved) and a non-empty, non-duplicate `expr`.\n - args (object) — Datasource-specific query options keyed by the `.