Skip to content

feat: Playbook panels#2614

Open
adityathebe wants to merge 3 commits into
mainfrom
feat/playbook-panel
Open

feat: Playbook panels#2614
adityathebe wants to merge 3 commits into
mainfrom
feat/playbook-panel

Conversation

@adityathebe
Copy link
Copy Markdown
Member

@adityathebe adityathebe commented Dec 9, 2025

resolves: #2294

Summary by CodeRabbit

  • New Features

    • Added a Playbooks panel type so views can display and act on playbooks via resource selectors.
    • Views are now considered valid when they contain only Playbooks panels (no sections or queries required).
  • Documentation

    • Added an example view demonstrating a Playbooks panel.
  • Chores

    • Updated project dependencies.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 9, 2025

Warning

Rate limit exceeded

@adityathebe has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3f54544 and 3144d12.

📒 Files selected for processing (9)
  • api/v1/view_types.go
  • api/view_panel.go
  • api/zz_generated.deepcopy.go
  • config/crds/mission-control.flanksource.com_views.yaml
  • config/schemas/view.schema.json
  • fixtures/views/panels/playbook-run.yaml
  • go.mod
  • views/run.go
  • views/table.go

Walkthrough

Adds a new "playbooks" panel type across API types, CRD/schema, and runtime: PlaybooksPanelConfig + selector, view validation updated to allow playbooks-only views, view runner executes playbooks selectors with RLS-backed SQL, fixtures added, and module deps updated.

Changes

Cohort / File(s) Summary
API types & deepcopy
api/v1/view_types.go, api/view_panel.go, api/zz_generated.deepcopy.go
Added PlaybooksPanelConfig (selector), PanelTypePlaybooks constant, PanelMeta.Playbooks field; updated ViewSpec validation to accept playbooks-only views; imported lo; generated deep-copy methods.
CRD & JSON schema
config/crds/..._views.yaml, config/schemas/view.schema.json
Introduced playbooks panel schema and PlaybooksPanelConfig definition, added playbooks to panel type enum, removed query from required panel fields, and added validations to restrict playbooks config to type playbooks and adjust global view validations.
View execution & runtime
views/run.go, views/table.go
Implemented playbooks panel execution: resolve selector IDs, build parameterized SQL with ID filter and deleted_at check, execute under auth.WithRLS; adjusted gating for SQL execution and replaced valueFromMaxResults with variableMaxResultForValueFrom.
Fixtures
fixtures/views/panels/playbook-run.yaml
Added example View manifest using a playbooks panel with a selector.
Dependencies
go.mod
Added/promoted github.com/Masterminds/squirrel and other dependency adjustments.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant ViewRunner as View Runner
    participant SelectorSvc as Resource Selector Resolver
    participant Auth as auth.WithRLS
    participant DB as Database

    User->>ViewRunner: request render view
    ViewRunner->>ViewRunner: iterate panels
    alt panel.type == "playbooks"
        ViewRunner->>SelectorSvc: resolve selector -> matching IDs
        SelectorSvc-->>ViewRunner: list of IDs
        ViewRunner->>Auth: build SQL with ID filters + RLS context
        Auth->>DB: execute parameterized SELECT WHERE id IN (...)
        DB-->>Auth: rows
        Auth-->>ViewRunner: result rows
    else other panel types
        ViewRunner->>Auth: execute panel.Query via RunSQL
        Auth->>DB: execute SQL
        DB-->>Auth: rows
        Auth-->>ViewRunner: result rows
    end
    ViewRunner-->>User: composed view results
Loading

Possibly related PRs

  • feat: View Sections #2571 — Modifies ViewSpec validation logic; touches the same validation short-circuit for playbooks/sections.
  • feat/view rls #2538 — Related changes to views runner and table code integrating RLS/auth and grants.

Suggested labels

ready

Suggested reviewers

  • moshloop

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Playbook panels' directly and concisely describes the main change—adding support for a new playbooks panel type to views.
Linked Issues check ✅ Passed The PR addresses only the 'playbooks (to run)' objective from issue #2294, implementing the playbooks panel type with configuration and execution logic.
Out of Scope Changes check ✅ Passed All changes relate to implementing the playbooks panel feature. The internal renaming of valueFromMaxResults and go.mod dependency adjustment are incidental supporting changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
views/run.go (1)

150-190: Consider documenting or making the playbook selector limit configurable.

The hardcoded limit of 200 on line 160 may silently truncate results if a selector matches more playbooks. Consider either:

  1. Documenting this limit in the CRD/API
  2. Making it configurable via the panel config
  3. Using a larger default or warning when the limit is reached

Otherwise, the implementation is sound - RLS protection is correctly applied, and the squirrel query builder usage is appropriate.

+// playbookSelectorLimit is the maximum number of playbooks to fetch when filtering by selector
+const playbookSelectorLimit = 200
+
 ...
-				ids, err := query.FindPlaybookIDsByResourceSelector(ctx, 200, panel.Playbooks.Selector)
+				ids, err := query.FindPlaybookIDsByResourceSelector(ctx, playbookSelectorLimit, panel.Playbooks.Selector)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0552720 and 50eba2d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • api/v1/view_types.go (3 hunks)
  • api/view_panel.go (4 hunks)
  • api/zz_generated.deepcopy.go (2 hunks)
  • config/crds/mission-control.flanksource.com_views.yaml (5 hunks)
  • config/schemas/view.schema.json (3 hunks)
  • fixtures/views/panels/playbook-run.yaml (1 hunks)
  • go.mod (2 hunks)
  • views/run.go (4 hunks)
  • views/table.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)
If a file needs to use both context and duty's context, alias context to 'gocontext'
Prefer any over interface{} in Go type declarations
Use ctx.Oops() to craft new errors with error codes from github.com/flanksource/duty/api as tags
Use dutyAPI.WriteError(c, err) in HTTP handlers to return errors with proper HTTP status code mapping based on error codes
For validation errors with no underlying error in HTTP handlers, use dutyAPI.Errorf(dutyAPI.EINVALID, "message")
For wrapping database/internal errors in HTTP handlers, use ctx.Oops().Wrap(err) or ctx.Oops().Wrapf(err, "context")
For permission errors in HTTP handlers, use ctx.Oops().Code(dutyAPI.EFORBIDDEN).Errorf("message")
Use duty.Now() instead of time.Now() for database timestamps and soft deletes
Only add comments if really necessary. Do not add comments that simply explain the code. Exception: comments about functions are considered good practice in Go even if they are self-explanatory

Files:

  • views/run.go
  • api/v1/view_types.go
  • views/table.go
  • api/zz_generated.deepcopy.go
  • api/view_panel.go
api/v1/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

CRD definitions must be located in the api/v1 directory

Files:

  • api/v1/view_types.go
**/*zz_generated.deepcopy.go

📄 CodeRabbit inference engine (AGENTS.md)

Never manually update zz_generated.deepcopy.go files. Use make generate instead

Files:

  • api/zz_generated.deepcopy.go
🧠 Learnings (4)
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `duty.Now()` instead of `time.Now()` for database timestamps and soft deletes

Applied to files:

  • views/run.go
  • go.mod
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)

Applied to files:

  • views/run.go
  • go.mod
  • api/v1/view_types.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Migrations are handled by the external package `github.com/flanksource/duty` using Atlas-go

Applied to files:

  • views/run.go
  • go.mod
  • api/v1/view_types.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `ctx.Oops()` to craft new errors with error codes from `github.com/flanksource/duty/api` as tags

Applied to files:

  • go.mod
  • api/v1/view_types.go
🧬 Code graph analysis (3)
views/run.go (2)
api/view_panel.go (1)
  • PanelTypePlaybooks (20-20)
auth/rls.go (1)
  • WithRLS (55-77)
api/v1/view_types.go (1)
api/view_panel.go (2)
  • PanelDef (32-37)
  • PanelTypePlaybooks (20-20)
api/zz_generated.deepcopy.go (1)
api/view_panel.go (1)
  • PlaybooksPanelConfig (40-43)
🔇 Additional comments (19)
go.mod (1)

15-15: LGTM! Dependency updates align with the feature.

The duty library bump and promoting squirrel to a direct dependency are appropriate for the Playbooks panel feature.

Also applies to: 41-41

api/zz_generated.deepcopy.go (1)

332-336: Verify this file was regenerated using make generate.

Per coding guidelines, zz_generated.deepcopy.go files should never be manually updated. The changes look correct for the new PlaybooksPanelConfig type, but please confirm this file was regenerated via make generate rather than manually edited.

Also applies to: 401-415

api/v1/view_types.go (3)

14-14: LGTM!

The lo import is used appropriately for filtering panels.


66-66: LGTM!

The XValidation rule correctly allows views with playbooks panels to bypass the sections/queries requirement.


138-145: Clarify: Does a view combining playbooks panels with sections require queries?

The validation logic at line 141 explicitly excludes views with sections when determining if a view has only playbooks panels. This means views combining playbooks panels and sections would not return early and would fall through to subsequent validation. Confirm whether this combination (playbooks panels + sections without queries) is intentionally invalid or if the condition should be adjusted.

config/schemas/view.schema.json (2)

480-492: LGTM!

The schema correctly adds the playbooks property and removes query from required fields, allowing playbooks panels that don't require a SQL query.


550-561: LGTM!

The PlaybooksPanelConfig definition is correctly structured with selector as a required property referencing the ResourceSelector type.

api/view_panel.go (3)

30-31: LGTM!

The XValidation rules correctly enforce:

  1. playbooks config is only allowed when type == 'playbooks'
  2. query is required for all non-playbooks panel types

39-43: LGTM!

The PlaybooksPanelConfig struct is well-defined with the +kubebuilder:object:generate=true marker for deepcopy generation and uses the appropriate types.ResourceSelector for the selector field.


71-73: LGTM!

The Playbooks field follows the same optional pointer pattern as other panel configurations (Gauge, Piechart, etc.), maintaining consistency.

views/table.go (2)

292-292: LGTM!

The constant rename to variableMaxResultForValueFrom is consistent with the definition in views/run.go and improves clarity about the constant's purpose.


537-537: LGTM!

Consistent usage of the renamed constant.

fixtures/views/panels/playbook-run.yaml (1)

1-16: LGTM!

This fixture correctly demonstrates a playbooks-only view with the new panel type. The structure aligns with the CRD schema changes and validation rules that allow views containing only playbooks panels without traditional queries.

views/run.go (3)

28-29: LGTM!

Good rename with a clear descriptive comment explaining the constant's purpose.


122-122: LGTM!

Correct logic update: SQLite is only needed when there are both panels and queries (for panel SQL queries against query results), or when there's a merge query. Playbooks panels query Postgres directly and don't need the in-memory SQLite database.


177-183: Cannot verify function existence due to repository access limitations.

The review comment requests verification of query.FindPlaybookIDsByResourceSelector from the duty package; however, the provided code snippet at lines 177-183 does not show a call to this function—it shows a dataquery.RunSQL call within auth.WithRLS. This discrepancy suggests the review comment may reference different code than what was provided. Verify that the function signature and location are correctly identified before proceeding with this review.

config/crds/mission-control.flanksource.com_views.yaml (3)

493-558: LGTM!

The playbooks panel configuration schema is well-defined. It correctly:

  • Reuses the ResourceSelector type for consistency
  • Marks selector as required to ensure valid playbook targeting
  • Follows the same pattern as other resource selectors in the CRD

594-597: LGTM!

The validation rules are correctly structured:

  • Line 594-595: Ensures playbooks config is only present when type == 'playbooks'
  • Line 596-597: Makes query required for all panel types except playbooks, since playbooks panels use their selector instead

1420-1426: LGTM!

The top-level validation correctly allows views that contain only playbooks panels without requiring traditional sections or queries. This enables the use case demonstrated in the fixture file.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
views/run.go (1)

153-183: Playbooks panel implementation looks correct.

The implementation properly:

  1. Builds the query with explicit column selection
  2. Filters by selector when provided (using FindPlaybookIDsByResourceSelector)
  3. Executes within RLS context for proper access control
  4. Uses parameterized queries with Dollar placeholders

One consideration: The hardcoded limit of 200 on line 160 for FindPlaybookIDsByResourceSelector may be worth making configurable via the selector's Limit field if that's supported.

 			if panel.Playbooks != nil && !panel.Playbooks.Selector.IsEmpty() {
-				ids, err := query.FindPlaybookIDsByResourceSelector(ctx, 200, panel.Playbooks.Selector)
+				limit := 200
+				if panel.Playbooks.Selector.Limit > 0 {
+					limit = panel.Playbooks.Selector.Limit
+				}
+				ids, err := query.FindPlaybookIDsByResourceSelector(ctx, limit, panel.Playbooks.Selector)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0552720 and 50eba2d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • api/v1/view_types.go (3 hunks)
  • api/view_panel.go (4 hunks)
  • api/zz_generated.deepcopy.go (2 hunks)
  • config/crds/mission-control.flanksource.com_views.yaml (5 hunks)
  • config/schemas/view.schema.json (3 hunks)
  • fixtures/views/panels/playbook-run.yaml (1 hunks)
  • go.mod (2 hunks)
  • views/run.go (4 hunks)
  • views/table.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)
If a file needs to use both context and duty's context, alias context to 'gocontext'
Prefer any over interface{} in Go type declarations
Use ctx.Oops() to craft new errors with error codes from github.com/flanksource/duty/api as tags
Use dutyAPI.WriteError(c, err) in HTTP handlers to return errors with proper HTTP status code mapping based on error codes
For validation errors with no underlying error in HTTP handlers, use dutyAPI.Errorf(dutyAPI.EINVALID, "message")
For wrapping database/internal errors in HTTP handlers, use ctx.Oops().Wrap(err) or ctx.Oops().Wrapf(err, "context")
For permission errors in HTTP handlers, use ctx.Oops().Code(dutyAPI.EFORBIDDEN).Errorf("message")
Use duty.Now() instead of time.Now() for database timestamps and soft deletes
Only add comments if really necessary. Do not add comments that simply explain the code. Exception: comments about functions are considered good practice in Go even if they are self-explanatory

Files:

  • views/run.go
  • api/v1/view_types.go
  • api/zz_generated.deepcopy.go
  • views/table.go
  • api/view_panel.go
api/v1/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

CRD definitions must be located in the api/v1 directory

Files:

  • api/v1/view_types.go
**/*zz_generated.deepcopy.go

📄 CodeRabbit inference engine (AGENTS.md)

Never manually update zz_generated.deepcopy.go files. Use make generate instead

Files:

  • api/zz_generated.deepcopy.go
🧠 Learnings (4)
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `duty.Now()` instead of `time.Now()` for database timestamps and soft deletes

Applied to files:

  • views/run.go
  • go.mod
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)

Applied to files:

  • views/run.go
  • api/v1/view_types.go
  • go.mod
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Migrations are handled by the external package `github.com/flanksource/duty` using Atlas-go

Applied to files:

  • views/run.go
  • api/v1/view_types.go
  • go.mod
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `ctx.Oops()` to craft new errors with error codes from `github.com/flanksource/duty/api` as tags

Applied to files:

  • api/v1/view_types.go
  • go.mod
🧬 Code graph analysis (3)
views/run.go (2)
api/view_panel.go (1)
  • PanelTypePlaybooks (20-20)
auth/rls.go (1)
  • WithRLS (55-77)
api/v1/view_types.go (1)
api/view_panel.go (2)
  • PanelDef (32-37)
  • PanelTypePlaybooks (20-20)
api/zz_generated.deepcopy.go (1)
api/view_panel.go (1)
  • PlaybooksPanelConfig (40-43)
🔇 Additional comments (15)
go.mod (1)

15-15: LGTM!

Dependency updates are aligned with the new playbooks panel feature. The squirrel library is appropriately promoted to a direct dependency since it's now used for SQL query construction in the playbooks panel logic.

Also applies to: 41-41

api/zz_generated.deepcopy.go (1)

1-3: Autogenerated file - no manual changes required.

This file is correctly auto-generated by controller-gen. The deepcopy functions for PlaybooksPanelConfig and the Playbooks field in PanelMeta are properly generated. As per coding guidelines, ensure this file was generated using make generate and not manually edited.

views/table.go (1)

292-292: LGTM!

The constant rename from valueFromMaxResults to variableMaxResultForValueFrom improves clarity by better describing its purpose for templating variable valueFrom queries.

Also applies to: 537-537

fixtures/views/panels/playbook-run.yaml (1)

1-16: LGTM!

Good fixture demonstrating a playbooks-only view. The selector correctly uses comma-separated names to match multiple playbooks (loki-logs,kubernetes-scale).

api/v1/view_types.go (2)

66-66: LGTM!

The XValidation rule correctly extends the validation to accept views that have only playbooks panels as a valid configuration alongside views with sections or queries.


138-145: LGTM!

The validation logic for playbooks-only views is well-implemented. It correctly filters non-playbook panels and allows views that contain only playbooks panels to bypass the query requirement.

config/schemas/view.schema.json (2)

480-492: LGTM!

Schema correctly adds the playbooks property and relaxes the required constraint to remove query, since playbooks panels don't require a query field.


550-561: LGTM!

PlaybooksPanelConfig is correctly defined with selector as a required field referencing ResourceSelector.

api/view_panel.go (2)

30-31: LGTM!

The XValidation rules are correctly implemented:

  • Playbooks config is only allowed for playbooks panel type
  • Query is required for all panel types except playbooks

39-43: LGTM!

PlaybooksPanelConfig is properly defined with a Selector field that allows filtering playbooks by resource selector criteria.

views/run.go (2)

122-122: LGTM!

The logic correctly avoids creating an in-memory SQLite database when not needed. Playbooks panels query PostgreSQL directly, so SQLite is only needed when there are both regular panels and queries defined.


185-190: LGTM!

The default case correctly handles non-playbooks panels using the existing SQLite-based query execution.

config/crds/mission-control.flanksource.com_views.yaml (3)

493-558: Playbooks panel schema follows established patterns with comprehensive selector fields.

The playbooks panel configuration reuses the standard resource selector pattern used elsewhere in the CRD (e.g., configTab in display.plugins). The required selector field appropriately constrains the panel to require explicit filtering criteria for playbook selection. All standard filter fields (agent, cache, fieldSelector, health, id, labelSelector, limit, name, namespace, scope, search, statuses, tagSelector, types) are available.

Verify that the required: [selector] constraint is intentional—i.e., there is no use case to render all playbooks without a selector. If a "show all" mode is needed, the required constraint should be removed.


594-597: Validation rules correctly enforce playbooks panel constraints.

Line 596–597 correctly inverts the query requirement: query is mandatory for all panel types except playbooks (where it is not required). This aligns with the playbooks panel's self-contained selector-based data source. The constraint on line 594–595 properly prevents playbooks config from appearing on non-playbooks panels.


1422-1423: Global view validation relaxed to permit playbooks-only views.

The validation rule now allows a view to satisfy the "content must be defined" constraint by including a playbooks panel, alongside sections or queries. This enables valid views with playbooks as their sole content source, which is consistent with the feature design.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
views/run.go (1)

185-190: Consider adding a nil check for sqliteCtx.

If a non-playbooks panel is processed but sqliteCtx is nil (e.g., due to validation being bypassed or future code changes), dataquery.RunSQL(sqliteCtx, panel.Query) could panic.

 			default:
+				if sqliteCtx == nil {
+					return nil, fmt.Errorf("failed to execute panel '%s': no SQL context available", panel.Name)
+				}
 				rows, err = dataquery.RunSQL(sqliteCtx, panel.Query)
 				if err != nil {
 					return nil, fmt.Errorf("failed to execute panel '%s': %w", panel.Name, err)
 				}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50eba2d and 29a4d21.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • api/v1/view_types.go (3 hunks)
  • api/view_panel.go (4 hunks)
  • api/zz_generated.deepcopy.go (2 hunks)
  • config/crds/mission-control.flanksource.com_views.yaml (5 hunks)
  • config/schemas/view.schema.json (3 hunks)
  • fixtures/views/panels/playbook-run.yaml (1 hunks)
  • go.mod (2 hunks)
  • views/run.go (4 hunks)
  • views/table.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • api/zz_generated.deepcopy.go
  • views/table.go
  • fixtures/views/panels/playbook-run.yaml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)
If a file needs to use both context and duty's context, alias context to 'gocontext'
Prefer any over interface{} in Go type declarations
Use ctx.Oops() to craft new errors with error codes from github.com/flanksource/duty/api as tags
Use dutyAPI.WriteError(c, err) in HTTP handlers to return errors with proper HTTP status code mapping based on error codes
For validation errors with no underlying error in HTTP handlers, use dutyAPI.Errorf(dutyAPI.EINVALID, "message")
For wrapping database/internal errors in HTTP handlers, use ctx.Oops().Wrap(err) or ctx.Oops().Wrapf(err, "context")
For permission errors in HTTP handlers, use ctx.Oops().Code(dutyAPI.EFORBIDDEN).Errorf("message")
Use duty.Now() instead of time.Now() for database timestamps and soft deletes
Only add comments if really necessary. Do not add comments that simply explain the code. Exception: comments about functions are considered good practice in Go even if they are self-explanatory

Files:

  • views/run.go
  • api/view_panel.go
  • api/v1/view_types.go
api/v1/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

CRD definitions must be located in the api/v1 directory

Files:

  • api/v1/view_types.go
🧠 Learnings (4)
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `duty.Now()` instead of `time.Now()` for database timestamps and soft deletes

Applied to files:

  • views/run.go
  • go.mod
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)

Applied to files:

  • views/run.go
  • go.mod
  • api/v1/view_types.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Migrations are handled by the external package `github.com/flanksource/duty` using Atlas-go

Applied to files:

  • views/run.go
  • go.mod
  • api/v1/view_types.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `ctx.Oops()` to craft new errors with error codes from `github.com/flanksource/duty/api` as tags

Applied to files:

  • go.mod
  • api/v1/view_types.go
🧬 Code graph analysis (2)
views/run.go (2)
api/view_panel.go (1)
  • PanelTypePlaybooks (18-18)
auth/rls.go (1)
  • WithRLS (55-77)
api/v1/view_types.go (1)
api/view_panel.go (2)
  • PanelDef (33-38)
  • PanelTypePlaybooks (18-18)
🔇 Additional comments (15)
go.mod (1)

15-15: LGTM!

The dependency updates are appropriate: duty patch bump and squirrel promotion to direct dependency align with the new Playbooks panel SQL query construction in views/run.go.

Also applies to: 41-41

api/v1/view_types.go (3)

14-14: LGTM!

Import addition for lo package is appropriate for the filtering operation below.


66-66: LGTM!

The updated validation rule correctly allows views that have playbooks panels as a valid alternative to sections or queries. The CEL expression self.panels.exists(p, p.type == 'playbooks') ensures at least one playbooks panel must exist for this path.


138-145: LGTM!

The validation logic correctly identifies playbooks-only views and bypasses the query requirement. The filtering approach using lo.Filter is clean, and the condition properly checks that no other view components (columns, queries, sections, non-playbook panels) are present.

views/run.go (2)

9-9: LGTM!

New imports are appropriate: squirrel for SQL query building, uuid for ID handling, and auth for RLS enforcement.

Also applies to: 19-19, 25-25


153-190: Code implementation is sound with proper security controls.

The playbooks panel handling is well-structured:

  • Uses parameterized queries via squirrel to prevent SQL injection
  • Enforces RLS via auth.WithRLS for proper access control
  • Filters out soft-deleted records with deleted_at IS NULL

Two observations to be aware of:

  1. Hardcoded limit (Line 160): The limit of 200 for FindPlaybookIDsByResourceSelector is hardcoded in the call. This appears to be a fixed parameter for this use case, but verify if it should be exposed as a configuration option.

  2. Empty selector behavior (Line 159): When panel.Playbooks is nil or the selector is empty, all non-deleted playbooks are returned without filtering. Confirm this is the intended behavior for this panel type.

config/schemas/view.schema.json (2)

480-492: LGTM!

The schema changes are correct:

  • Added playbooks property referencing PlaybooksPanelConfig
  • Removed query from required fields (appropriate since playbooks panels don't use queries)
  • Required fields now only include name and type

550-561: LGTM!

The PlaybooksPanelConfig definition correctly requires a selector field of type ResourceSelector. This aligns with the Go type definition in api/view_panel.go.

api/view_panel.go (5)

5-5: LGTM!

Import addition for types package is necessary for the ResourceSelector type used in PlaybooksPanelConfig.


13-22: LGTM!

Panel type constants are well-organized alphabetically with the new PanelTypePlaybooks added.


31-32: LGTM!

The validation rules correctly enforce:

  1. playbooks config is only allowed when type is playbooks
  2. query is required for all non-playbooks panel types

This ensures proper panel configuration based on type.


40-44: LGTM!

The PlaybooksPanelConfig struct is well-defined with proper kubebuilder marker for code generation. The Selector field correctly uses types.ResourceSelector for flexible playbook filtering.


54-56: LGTM!

The enum validation is updated to include playbooks, and PanelMeta correctly includes the optional Playbooks configuration pointer, consistent with other panel-specific configs.

Also applies to: 72-74

config/crds/mission-control.flanksource.com_views.yaml (2)

1420-1426: Global view validation correctly updated for playbooks-only views.

Line 1422–1423 now allows views to be valid when they contain playbooks panels, removing the hard requirement for sections or queries. This enables the new use case of playbooks-only views.

The logic correctly allows (sections OR queries OR playbooks_panel), which properly gates valid view configurations.


493-598: Schema integration for playbooks panel is complete and properly implemented.

The playbooks panel type is correctly integrated into the CRD with:

  • A well-defined PlaybooksPanelConfig with a required selector (lines 493–558)
  • Consistent validation enforcement: playbooks config only allowed for type: playbooks (lines 594–595), and query required only for non-playbooks types (lines 596–597)
  • The panel type enum properly updated (line 577)

The backend implementation is verified and working:

  • Panel execution handler in views/run.go (lines 153–170) uses FindPlaybookIDsByResourceSelector to fetch matching playbooks
  • Selector-based filtering properly applied with error handling for empty or failed selector queries
  • RLS filtering integrated through the context-aware selector query
  • Edge cases handled: empty selectors skip the WHERE clause, empty results return an empty slice, and spec parsing errors are logged without interrupting execution
  • Permissions tests confirm RLS behavior with playbooks

Comment thread views/run.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
views/run.go (1)

160-160: Consider making the playbook selector limit configurable or documented.

The hardcoded limit of 200 in FindPlaybookIDsByResourceSelector could silently truncate results if more playbooks match the selector. Consider either:

  • Making this configurable via the PlaybooksPanelConfig
  • Adding a log warning when the limit is reached
  • Documenting this limitation
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29a4d21 and 3f54544.

📒 Files selected for processing (9)
  • api/v1/view_types.go (3 hunks)
  • api/view_panel.go (4 hunks)
  • api/zz_generated.deepcopy.go (2 hunks)
  • config/crds/mission-control.flanksource.com_views.yaml (5 hunks)
  • config/schemas/view.schema.json (3 hunks)
  • fixtures/views/panels/playbook-run.yaml (1 hunks)
  • go.mod (1 hunks)
  • views/run.go (5 hunks)
  • views/table.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • views/table.go
  • go.mod
  • fixtures/views/panels/playbook-run.yaml
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)
If a file needs to use both context and duty's context, alias context to 'gocontext'
Prefer any over interface{} in Go type declarations
Use ctx.Oops() to craft new errors with error codes from github.com/flanksource/duty/api as tags
Use dutyAPI.WriteError(c, err) in HTTP handlers to return errors with proper HTTP status code mapping based on error codes
For validation errors with no underlying error in HTTP handlers, use dutyAPI.Errorf(dutyAPI.EINVALID, "message")
For wrapping database/internal errors in HTTP handlers, use ctx.Oops().Wrap(err) or ctx.Oops().Wrapf(err, "context")
For permission errors in HTTP handlers, use ctx.Oops().Code(dutyAPI.EFORBIDDEN).Errorf("message")
Use duty.Now() instead of time.Now() for database timestamps and soft deletes
Only add comments if really necessary. Do not add comments that simply explain the code. Exception: comments about functions are considered good practice in Go even if they are self-explanatory

Files:

  • views/run.go
  • api/view_panel.go
  • api/zz_generated.deepcopy.go
  • api/v1/view_types.go
**/*zz_generated.deepcopy.go

📄 CodeRabbit inference engine (AGENTS.md)

Never manually update zz_generated.deepcopy.go files. Use make generate instead

Files:

  • api/zz_generated.deepcopy.go
api/v1/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

CRD definitions must be located in the api/v1 directory

Files:

  • api/v1/view_types.go
🧠 Learnings (4)
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `duty.Now()` instead of `time.Now()` for database timestamps and soft deletes

Applied to files:

  • views/run.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use context from github.com/flanksource/duty/context.Context to access db (ctx.DB()), properties (ctx.Properties()), and logger (ctx.Logger)

Applied to files:

  • views/run.go
  • api/v1/view_types.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Migrations are handled by the external package `github.com/flanksource/duty` using Atlas-go

Applied to files:

  • views/run.go
  • api/v1/view_types.go
📚 Learning: 2025-11-24T18:35:17.129Z
Learnt from: CR
Repo: flanksource/mission-control PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T18:35:17.129Z
Learning: Applies to **/*.go : Use `ctx.Oops()` to craft new errors with error codes from `github.com/flanksource/duty/api` as tags

Applied to files:

  • api/v1/view_types.go
🧬 Code graph analysis (2)
views/run.go (2)
api/view_panel.go (1)
  • PanelTypePlaybooks (18-18)
auth/rls.go (1)
  • WithRLS (55-77)
api/zz_generated.deepcopy.go (1)
api/view_panel.go (1)
  • PlaybooksPanelConfig (41-44)
🔇 Additional comments (13)
api/zz_generated.deepcopy.go (1)

1-415: Auto-generated file looks correct.

The deep copy implementations for PlaybooksPanelConfig and the Playbooks field in PanelMeta are properly generated. The Selector field is correctly deep-copied using in.Selector.DeepCopyInto(&out.Selector).

As per coding guidelines, ensure this file is regenerated via make generate rather than manually edited.

api/v1/view_types.go (2)

66-66: XValidation rule correctly updated for playbooks panel support.

The validation rule now allows views with only playbooks panels to bypass the sections/queries requirement. The CEL expression self.panels.exists(p, p.type == 'playbooks') properly checks for the presence of playbooks-type panels.


138-145: Go-side validation correctly mirrors the XValidation rule.

The logic properly allows playbooks-only views to skip the query requirement:

  • Filters out playbook panels using lo.Filter
  • Checks if the view contains only playbook panels (no columns, queries, sections, or other panel types)
  • Returns early without requiring queries in this case

This aligns with the CRD validation and ensures consistent behavior.

config/schemas/view.schema.json (2)

480-492: Schema correctly adds playbooks panel support.

The PanelDef schema properly:

  • Adds the optional playbooks property referencing PlaybooksPanelConfig
  • Removes query from the required fields array (now only name and type are required)

This aligns with the Go validation rules where query is conditionally required based on panel type.


550-561: PlaybooksPanelConfig schema definition is correct.

The schema properly defines:

  • selector property referencing ResourceSelector
  • selector as a required field
  • No additional properties allowed

This matches the Go struct definition in api/view_panel.go.

views/run.go (2)

122-122: Good fix for the nil sqliteCtx issue.

The condition at line 139 now requires len(view.Spec.Queries) > 0 in addition to panels being present. This ensures sqliteCtx is always initialized (via needsSQL at line 122) before reaching the default panel case that calls dataquery.RunSQL(sqliteCtx, panel.Query).

This addresses the critical issue flagged in the previous review.

Also applies to: 139-139


153-183: Playbooks panel implementation is well-structured.

The implementation correctly:

  • Builds a parameterized query using squirrel with sq.Dollar placeholder format
  • Filters out soft-deleted playbooks with WHERE deleted_at IS NULL
  • Applies RLS via auth.WithRLS for proper access control
  • Wraps errors with panel context for debugging
api/view_panel.go (3)

31-32: XValidation rules correctly enforce panel type constraints.

The two validation rules work together properly:

  • Line 31: Prevents playbooks config on non-playbooks panel types
  • Line 32: Requires query for all panel types except playbooks

This ensures type-specific configuration is enforced at the CRD level.


40-44: PlaybooksPanelConfig struct is well-defined.

The struct correctly:

  • Uses types.ResourceSelector for flexible playbook selection
  • Has proper JSON/YAML tags
  • Includes the +kubebuilder:object:generate=true annotation for deep copy generation
  • Has a descriptive comment explaining the selector's purpose

54-56: Enum validation updated correctly.

The +kubebuilder:validation:Enum annotation now includes playbooks in the list of valid panel types, aligning with the new PanelTypePlaybooks constant.

config/crds/mission-control.flanksource.com_views.yaml (3)

493-558: Playbooks selector schema follows established patterns.

The selector definition correctly mirrors existing selector schemas (e.g., configTab.selector) with consistent field names, types, and documentation. The required selector field is appropriately marked.


594-597: CEL validation rules correctly enforce playbooks panel constraints.

The two validation rules properly enforce:

  1. Playbooks config only allowed for type == 'playbooks'
  2. Query field required for all non-playbooks panel types

The CEL syntax is correct and aligns with existing validation patterns in the file.


567-581: Verify that removing query from required fields doesn't break downstream code.

The change removes query from the panel's required fields list, allowing playbooks panels to omit the query field while other panel types enforce it via validation rules. This is correct in principle, but needs verification across the codebase to ensure no code paths assume query is always present on panels.

Key areas to verify:

  • Code that accesses panel.query without null/existence checks
  • Validation rules that enforce query for non-playbook panel types
  • Playbooks panel handling to confirm it correctly operates without a query field

Comment thread config/crds/mission-control.flanksource.com_views.yaml
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.

View | New Panels

1 participant