Skip to content

feat(validate): add SLI query validation#504

Draft
matkaras wants to merge 7 commits into
mainfrom
validate-sli-query
Draft

feat(validate): add SLI query validation#504
matkaras wants to merge 7 commits into
mainfrom
validate-sli-query

Conversation

@matkaras

@matkaras matkaras commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Release Notes

Add sloctl validate sli to validate SLI queries against Nobl9 data sources. It validates SLO YAML files with -f or existing SLOs by passing the SLO name, prints a concise validation summary by default, and returns full time-series data with -o yaml|json|csv.

Motivation

Users did not have a sloctl command to test SLI queries before applying or debugging SLO definitions, so query issues required checking elsewhere. This command uses the Nobl9 SDK data source query endpoint and keeps default output short while preserving structured time-series output when requested.

Summary

  • Add the validate sli command.
  • Support SLO YAML file input with -f, existing SLO lookup by name, SLO/objective filters, --last, --from, and --to.
  • Enforce a 1h maximum time range and a 50 query maximum.
  • Execute data source query requests in parallel while preserving output order.
  • Print a human summary by default with Valid/Invalid, query counts, time range, point counts, min, max, and errors.
  • Return full structured time-series output with -o yaml|json|csv.
  • Update nobl9-go to v0.133.1 for the data source query SDK endpoint.
sloctl validate sli dash0-accounting-client-success -p dash0-direct
⠼ Validating 2 SLI queries...  [5s]
Valid

Validated 2 SLI queries for 1 SLO.
Time range: 2026-07-03T11:30:30Z - 2026-07-03T11:45:30Z

dash0-accounting-client-success/dash0-direct notok countMetrics
  good: 16 points, min 0.01666555562962469, max 0.08333750020834375
  total: 16 points, min 0.01666555562962469, max 0.08333750020834375

dash0-accounting-client-success/dash0-direct ok countMetrics
  good: 16 points, min 0.024999479177517134, max 0.058333333333333334
  total: 16 points, min 0.024999479177517134, max 0.058333333333333334
sloctl validate sli dash0-accounting-client-success -p dash0-direct --last 2m -o yaml
⠇ Validating 2 SLI queries...  [3s]
timeRange:
  from: 2026-07-03T11:44:40.893164Z
  to: 2026-07-03T11:46:40.893164Z
results:
- slo: dash0-accounting-client-success
  project: dash0-direct
  objective: notok
  metric: countMetrics
  status: success
  timeseries:
  - name: good
    values:
    - - 1783079080
      - 0.08333611120370679
    - - 1783079140
      - 0.016666666666666666
    - - 1783079200
      - 0.016666666666666666
  - name: total
    values:
    - - 1783079080
      - 0.08333611120370679
    - - 1783079140
      - 0.016666666666666666
    - - 1783079200
      - 0.016666666666666666
- slo: dash0-accounting-client-success
  project: dash0-direct
  objective: ok
  metric: countMetrics
  status: success
  timeseries:
  - name: good
    values:
    - - 1783079080
      - 0.058333333333333334
    - - 1783079140
      - 0.05000020833420139
    - - 1783079200
      - 0.05000104168836851
  - name: total
    values:
    - - 1783079080
      - 0.058333333333333334
    - - 1783079140
      - 0.05000020833420139
    - - 1783079200
      - 0.05000104168836851
sloctl validate sli acv2-dynatrace-direct-dql-raw -p acv2-dynatrace-direct
⠋ Validating 1 SLI queries...  [3s]
Invalid

Validated 1 SLI query for 1 SLO.
Time range: 2026-07-03T11:55:34Z - 2026-07-03T12:10:34Z

acv2-dynatrace-direct-dql-raw/acv2-dynatrace-direct objective-1 rawMetric
  datasource command failed:
  Dynatrace client query failed: [https://apps.dynatrace.com/platform/storage/query/v1/query:execute] 400 Bad Request:
  {
    "error": {
      "message": "UNKNOWN_COMMAND",
      "details": {
        "exceptionType": "DQL-SYNTAX-ERROR",
        "errorType": "UNKNOWN_COMMAND",
        "errorMessage": "There's no command `ilter`.",
        "arguments": [
          "ilter"
        ],
        "queryString": "fetch spans | ilter service.name == \"frontend\" | makeTimeseries latency_p99 = percentile(duration, 99), interval:1m",
        "errorMessageFormatSpecifierTypes": [
          "INPUT_QUERY_PART"
        ],
        "errorMessageFormat": "There's no command `%1$s`.",
        "queryId": "1c6a9947-f81e-4a4e-bd77-d1b659211c47",
        "syntaxErrorPosition": {
          "start": {
            "column": 15,
            "index": 14,
            "line": 1
          },
          "end": {
            "column": 19,
            "index": 18,
            "line": 1
          }
        }
      },
      "code": 400
    }
  }
   (HTTP status code 400)
sloctl validate sli acv2-dynatrace-direct-dql-raw -p acv2-dynatrace-direct -o json
⠇ Validating 1 SLI queries...  [1s]
{
  "timeRange": {
    "from": "2026-07-03T11:57:33.190703Z",
    "to": "2026-07-03T12:12:33.190703Z"
  },
  "results": [
    {
      "slo": "acv2-dynatrace-direct-dql-raw",
      "project": "acv2-dynatrace-direct",
      "objective": "objective-1",
      "metric": "rawMetric",
      "status": "failed",
      "errors": [
        {
          "title": "datasource command failed",
          "code": "datasource_command_failed",
          "detail": "Dynatrace client query failed: [https://apps.dynatrace.com/platform/storage/query/v1/query:execute] 400 Bad Request: {\"error\":{\"message\":\"UNKNOWN_COMMAND\",\"details\":{\"exceptionType\":\"DQL-SYNTAX-ERROR\",\"errorType\":\"UNKNOWN_COMMAND\",\"errorMessage\":\"There's no command `ilter`.\",\"arguments\":[\"ilter\"],\"queryString\":\"fetch spans | ilter service.name == \\\"frontend\\\" | makeTimeseries latency_p99 = percentile(duration, 99), interval:1m\",\"errorMessageFormatSpecifierTypes\":[\"INPUT_QUERY_PART\"],\"errorMessageFormat\":\"There's no command `%1$s`.\",\"queryId\":\"c5e8878c-967d-4d36-b7ae-a6556b5bd82c\",\"syntaxErrorPosition\":{\"start\":{\"column\":15,\"index\":14,\"line\":1},\"end\":{\"column\":19,\"index\":18,\"line\":1}}},\"code\":400}} (HTTP status code 400)"
        }
      ]
    }
  ]
}

Related Changes

None.

Testing

  • Validate an existing SLO by passing its name and project; confirm default output shows Valid, time range, point counts, min, and max.
  • Validate an SLO YAML file with -f; confirm each objective appears in the summary.
  • Validate an SLO with an invalid query; confirm output shows Invalid, includes backend error details, and exits non-zero.
  • Run with -o json, -o yaml, and -o csv; confirm output includes full [timestamp, value] time-series values.
  • Run with --last above 1h and with a --from/--to range above 1h; confirm the command rejects the time range.

@github-actions github-actions Bot added the go label Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant