Skip to content

[dmt] linter for checking rules and dashboards for source=deckhouse label#371

Draft
rvekaterina wants to merge 6 commits into
mainfrom
feat/source-label-linter
Draft

[dmt] linter for checking rules and dashboards for source=deckhouse label#371
rvekaterina wants to merge 6 commits into
mainfrom
feat/source-label-linter

Conversation

@rvekaterina
Copy link
Copy Markdown

No description provided.

@diyliv diyliv assigned diyliv and unassigned diyliv May 25, 2026
@diyliv diyliv self-requested a review May 25, 2026 13:45
Copy link
Copy Markdown
Contributor

@diyliv diyliv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fill the pr description in this format:

Summary

<what the pr does, bullet points>

Example

<usage / config examples>


var (
grafanaBuiltinVarRe = regexp.MustCompile(`\$__\w+`)
grafanaVarBracesRe = regexp.MustCompile(`\$\{(\w+)(?::[^}]*)?\}`)
Copy link
Copy Markdown
Contributor

@diyliv diyliv May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

${source} and ${source:json} get replaced with placeholder because this regex runs first and blindly replaces everything, no check on the variable name. the simple $source form is fine below (lines 253-255) - it checks name == "source" and preserves it. but the braced form has no such check.

to reproduce: dashboard with m{source="${source}"}, after sanitization you get m{source="placeholder"}, checkExpr doesn't see source="deckhouse" and complains. TestSanitizeGrafanaExpr only tests $source, ${source} isn't covered at all.


func (r *SourceLabelRule) checkExpr(expr, ruleName, groupName, filePath string, errorList *errors.LintRuleErrorsList) {
ast, err := parser.ParseExpr(expr)
if err != nil {
Copy link
Copy Markdown
Contributor

@diyliv diyliv May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

promql parse errors are just eaten. if a rule has a broken expr or the sanitized grafana expression is garbage - the linter silently moves on. no error, no warning.

}

func (r *SourceLabelRule) extractDashboardPanels(dashboard *gjson.Result) []gjson.Result {
panels := make([]gjson.Result, 0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only one level of row -> panels is unwrapped. if a dashboard has a row inside another row, checkPanel never reaches the panels at the second level.

example: panels[0] (type=row) -> inside it panels[0].panels[0] (type=row) -> inside that panels[0].panels[0].panels[0] (the actual panel with targets). the linter skips it.

}

func isPrometheusDatasource(obj *gjson.Result) bool {
dsType := obj.Get("datasource.type").String()
Copy link
Copy Markdown
Contributor

@diyliv diyliv May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when datasource is a plain string, not an object (e.g. datasource: "Graphite"), datasource.type is missing, dsType is empty, the function returns true - claims it's prometheus. in practice ParseExpr silently fails on foreign syntax and produces no errors, so no real harm, but we're burning cpu parsing non-promql stuff.

grafanaVarSimpleRe = regexp.MustCompile(`\$([a-zA-Z_]\w*)`)
)

func sanitizeGrafanaExpr(expr string) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a grafana variable sits where a metric name should be (not a label value), it gets replaced with placeholder, checkExpr parses it as a metric name, doesn't find source="deckhouse", and fires an error.

example: panel expression $my_metric{some="label"} -> placeholder{some="label"} -> false positive.

r.checkTemplateVariables(&dashboard, filePath, errorList)
}

func isPrometheusDatasource(obj *gjson.Result) bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

casing: isPrometheusDatasource -> should be isPrometheusDataSource (lowercase s in camelCase, not uppercase S).

@diyliv diyliv added enhancement New feature or request go Pull requests that update go code labels May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants