Skip to content

ROX-34665: Add Scans per Day by Namespace panel to cluster overview - #340

Open
ebensh wants to merge 3 commits into
masterfrom
rox-34665-scans-per-day
Open

ROX-34665: Add Scans per Day by Namespace panel to cluster overview#340
ebensh wants to merge 3 commits into
masterfrom
rox-34665-scans-per-day

Conversation

@ebensh

@ebensh ebensh commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a new "Scans per Day by Namespace" table panel to the rhacs-cluster-overview Grafana dashboard
  • Panel placed directly after the Scanner Overview Table at full width (w=24)
  • Uses increase(rox_central_scan_duration_count[1d]) with matrix transform to show daily scan counts per namespace as date columns
image

Details

The panel uses the existing $_relativeTime and $_timeShift dashboard variables to control the time window. Results are formatted as a pivot table with namespaces as rows and dates (YYYY-MM-DD) as columns.

Test plan

  • Verify panel renders correctly in Grafana staging
  • Confirm date columns populate with scan count data
  • Check panel positioning relative to Scanner Overview Table and Certificate Expiry section

🔗 ROX-34665

…ashboard

Add table panel showing daily scan counts broken down by namespace to
rhacs-cluster-overview dashboard, placed after Scanner Overview Table.
Uses increase(rox_central_scan_duration_count[1d]) with matrix transform
to display dates as columns.
@ebensh
ebensh requested a review from a team as a code owner May 15, 2026 09:52
@ebensh
ebensh requested review from kovayur and kurlov and removed request for a team and kovayur May 15, 2026 09:52
@GrimmiMeloni

Copy link
Copy Markdown

Could we also chart this to allow spotting spikes quickly?

@@ -0,0 +1,12 @@
{

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.

Can we add this file to .gitignore?

@kovayur

kovayur commented May 18, 2026

Copy link
Copy Markdown
Contributor

Agree with @GrimmiMeloni , a chart would be more representative than a table

@ebensh

ebensh commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

Could we also chart this to allow spotting spikes quickly?

Agree with @GrimmiMeloni , a chart would be more representative than a table

I originally had a continuous plot of the metric and asked about this but the answer I got was:

I wanted to answer how many scans does a customer do in a given day. Just to know how their usage patterns look like.

So I converted to a table aligned to midnight for the daily counts. I can of course go back to a simple increase()[1d] for the rolling amount. Do we want the table and the plot or just the plot?

@GrimmiMeloni

Copy link
Copy Markdown

I think just the plot would be fine. It's ok if it only shows the daily number like in the table. The whole idea was to simply have something to "eyeball" the usage patterns.

@kovayur

kovayur commented May 19, 2026

Copy link
Copy Markdown
Contributor

How about a stacked bar chart with dates on the X-axis?

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a Grafana table showing scan activity over the past 24 hours, organized by namespace and date.
    • Adjusted dashboard layout to accommodate the new scan activity panel.

Walkthrough

The Grafana dashboard now includes daily scan counts grouped by namespace. Existing Certificate Expiry panels move down to make space. A local Claude settings file adds workspace read access and selected command permissions.

Changes

Grafana scan panel

Layer / File(s) Summary
Daily namespace scan table
resources/grafana/sources/rhacs-cluster-overview.json
Adds a table panel that displays daily increases of rox_central_scan_duration_count by namespace and date.
Dashboard panel layout
resources/grafana/sources/rhacs-cluster-overview.json
Moves the Certificate Expiry row and table below the new scan panel.

Local Claude permissions

Layer / File(s) Summary
Claude command permissions
resources/grafana/sources/.claude/settings.local.json
Adds workspace read access and allowlisted status, rebase, cleanup, directory-removal, and commit commands.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: grimmimeloni, johannes94, kovayur, kurlov

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the addition of the Scans per Day by Namespace panel to the cluster overview dashboard.
Description check ✅ Passed The description directly explains the new Grafana panel, its query, layout, transformation, variables, and test plan.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rox-34665-scans-per-day

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@resources/grafana/sources/.claude/settings.local.json`:
- Line 6: Update the permissions configuration entry for Bash(jj rebase *) to
remove the broad rule or replace it with a narrowly scoped permission such as
Bash(jj rebase --continue *) that permits only the intended workflow.
- Line 9: Update the Bash permission rule in the local Claude settings so the
commit-message quote is properly closed before the wildcard suffix, making the
pattern match the intended quoted jj commit command exactly.

In `@resources/grafana/sources/rhacs-cluster-overview.json`:
- Line 2227: Update the Prometheus expression in the scan-count panel to filter
rox_central_scan_duration_count by the selected $instance_id label before
aggregating by namespace, matching the namespace scoping used by existing
dashboard panels.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d1e4ecb-efac-43fb-99fb-e715a43b6790

📥 Commits

Reviewing files that changed from the base of the PR and between b7b4ed5 and 1f40b65.

⛔ Files ignored due to path filters (1)
  • resources/grafana/generated/dashboards/rhacs-cluster-overview.yaml is excluded by !**/generated/**
📒 Files selected for processing (2)
  • resources/grafana/sources/.claude/settings.local.json
  • resources/grafana/sources/rhacs-cluster-overview.json

"allow": [
"Read(//home/ebenshet/workspaces/rox-34665-grafana-display-scans-per-day/github.com/stackrox/rhacs-observability-resources/**)",
"Bash(jj status *)",
"Bash(jj rebase *)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python - <<'PY'
import json

path = "resources/grafana/sources/.claude/settings.local.json"
with open(path, encoding="utf-8") as stream:
    rules = json.load(stream)["permissions"]["allow"]

matches = [rule for rule in rules if rule.startswith("Bash(jj rebase")]
if not matches:
    raise SystemExit("No jj rebase rule found")

for rule in matches:
    print(rule)
    if rule == "Bash(jj rebase *)":
        raise SystemExit("Unscoped jj rebase permission found")
PY

Repository: stackrox/rhacs-observability-resources

Length of output: 230


🌐 Web query:

Claude Code permissions allow rules Bash command glob prefix Bash command:*

💡 Result:

In Claude Code, Bash permission rules support glob pattern matching using the asterisk (*) character [1][2]. These rules allow you to specify which shell commands Claude is permitted to execute, blocked from executing (deny), or required to ask about (ask) [3][4]. Key details regarding the use of glob patterns in Bash rules: Positioning and Matching: Wildcards can be placed at the beginning, middle, or end of a command string [1][3]. - Bash(npm ) matches any command starting with "npm " [1][3]. - Bash( install) matches any command ending in " install" [1][3]. - Bash(git * main) matches commands like "git checkout main" or "git log --oneline main" [1][3]. Word Boundaries: The placement of a space before the asterisk is significant because it enforces word boundaries [1][2]. - Bash(ls ) matches "ls -la" but does not match "lsof" [1][2]. - Bash(ls) matches both "ls -la" and "lsof" [1][2]. Alternative Suffix: The:* suffix is functionally equivalent to a trailing wildcard [1][2]. For example, Bash(ls:*) matches the same commands as Bash(ls *) [1][2]. Important Behaviors: - Compound Commands: Claude Code parses compound commands (e.g., those using && or ||) individually. A Bash rule such as Bash(safe-cmd *) does not automatically permit a command like "safe-cmd && rm -rf." because each subcommand must match the permission rules independently [3]. - Rule Evaluation: Rules are evaluated in the order of deny, then ask, then allow [4]. The first matching rule determines the outcome [4]. A deny rule is the strongest and will block a command even if a more specific allow rule exists [4]. - Tool Removal vs. Scoping: A deny rule specified with just the tool name (e.g., "Bash") removes the tool entirely from Claude's context [1][3]. A scoped deny rule (e.g., "Bash(rm *)") keeps the tool available but blocks only the matching command pattern [3][4]. These rules are typically configured in your.claude/settings.json or ~/.claude/settings.json files [5][6].

Citations:


Narrow the jj rebase permission.

Bash(jj rebase *) permits arbitrary jj rebase options and subcommands. Remove this rule or replace it with a specific command such as Bash(jj rebase --continue *) for the exact workflow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/grafana/sources/.claude/settings.local.json` at line 6, Update the
permissions configuration entry for Bash(jj rebase *) to remove the broad rule
or replace it with a narrowly scoped permission such as Bash(jj rebase
--continue *) that permits only the intended workflow.

Source: Path instructions

"Bash(jj rebase *)",
"Bash(rm -f resources/grafana/generated/dashboards/new-panel.yaml resources/grafana/sources/.claude/settings.local.json)",
"Bash(rmdir resources/grafana/sources/.claude)",
"Bash(jj commit -m 'ROX-34665: Add Scans per Day by Namespace panel to cluster overview dashboard *)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python - <<'PY'
import json

path = "resources/grafana/sources/.claude/settings.local.json"
with open(path, encoding="utf-8") as stream:
    rules = json.load(stream)["permissions"]["allow"]

rule = next(rule for rule in rules if rule.startswith("Bash(jj commit"))
body = rule[len("Bash("):-1]

if body.count("'") % 2:
    raise SystemExit("Commit permission has unbalanced single quotes")
if body.endswith(" *"):
    raise SystemExit("Commit permission is broader than the exact command")
PY

Repository: stackrox/rhacs-observability-resources

Length of output: 223


🌐 Web query:

Claude Code IAM Bash rules exact Bash(command) prefix Bash(command:*)

💡 Result:

Claude Code uses flexible pattern matching for Bash command permissions, supporting exact matches, wildcard patterns, and a specialized cmd: specifier [1][2]. ### Bash Permission Matching Syntax Claude Code evaluates rules based on the following patterns: * Exact Matches: Bash(npm run build) allows only that specific command [3][2]. * Wildcard Patterns: Use * to match arbitrary characters [3]. * Bash(npm *): Matches any command starting with npm . * Bash(* install): Matches any command ending with install [3][4]. * Bash(git * main): Matches commands with mid-string wildcards, such as git checkout main [3]. * Note: The presence of a space before the * is significant. Bash(ls *) matches ls -la but not lsof, whereas Bash(ls*) matches both [3][2]. * Legacy Prefix Syntax: The :* suffix (e.g., Bash(git:*)) is a legacy, deprecated syntax equivalent to using a wildcard space pattern [1][2]. ### The cmd: Specifier The cmd: prefix matches against the base command name, providing resilience against quoted arguments, shell operators, and special characters [2]. * Bash(cmd:git): Matches any invocation where the base command is git, regardless of arguments [2]. * Bash(cmd:git *): Matches any git command followed by any arguments [2]. * Bash(cmd:*): Matches all Bash commands (functionally equivalent to Bash) [2]. ### Important Considerations * Compound Commands: Claude Code recognizes shell operators (&&, ||, ;, |, |&, &, newlines). Rules must match each subcommand independently; a rule like Bash(safe-cmd *) does not grant permission to execute safe-cmd && dangerous-cmd [3][1]. * Security Warning: Bash permission patterns are fragile and should not be relied upon as a primary security boundary [5]. They can often be bypassed by flag reordering, protocol changes, or using shell variables [5]. * Automatic Stripping: Safe wrapper commands (e.g., timeout, nice) and safe environment variable assignments are automatically stripped before matching so that permission rules apply to the core command [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,40p' resources/grafana/sources/.claude/settings.local.json | cat -n

Repository: stackrox/rhacs-observability-resources

Length of output: 789


Fix the commit permission pattern.

The rule opens a single quote before the commit message but never closes it before *). It will not match the intended quoted commit command.

Use an exact rule:

Proposed fix
-      "Bash(jj commit -m 'ROX-34665: Add Scans per Day by Namespace panel to cluster overview dashboard *)"
+      "Bash(jj commit -m 'ROX-34665: Add Scans per Day by Namespace panel to cluster overview dashboard')"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"Bash(jj commit -m 'ROX-34665: Add Scans per Day by Namespace panel to cluster overview dashboard *)"
"Bash(jj commit -m 'ROX-34665: Add Scans per Day by Namespace panel to cluster overview dashboard')"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/grafana/sources/.claude/settings.local.json` at line 9, Update the
Bash permission rule in the local Claude settings so the commit-message quote is
properly closed before the wildcard suffix, making the pattern match the
intended quoted jj commit command exactly.

},
"editorMode": "code",
"exemplar": false,
"expr": "sum by (namespace) (increase(rox_central_scan_duration_count[1d]))",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restrict the query to the dashboard namespace scope.

Line 2227 aggregates scans from every namespace in the datasource. It ignores the $instance_id selection that existing dashboard panels use. This can show unrelated scan counts and increases query cost.

Proposed fix
- "expr": "sum by (namespace) (increase(rox_central_scan_duration_count[1d]))",
+ "expr": "sum by (namespace) (increase(rox_central_scan_duration_count{namespace=~\"rhacs-$instance_id\"}[1d]))",

As per path instructions, focus on “major issues impacting performance, readability, maintainability and security.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"expr": "sum by (namespace) (increase(rox_central_scan_duration_count[1d]))",
"expr": "sum by (namespace) (increase(rox_central_scan_duration_count{namespace=~\"rhacs-$instance_id\"}[1d]))",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/grafana/sources/rhacs-cluster-overview.json` at line 2227, Update
the Prometheus expression in the scan-count panel to filter
rox_central_scan_duration_count by the selected $instance_id label before
aggregating by namespace, matching the namespace scoping used by existing
dashboard panels.

Source: Path instructions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants