Skip to content

feat(api): add Option resource and x-cozystack-options for dynamic form dropdowns - #2778

Merged
Aleksei Sviridkin (lexfrei) merged 20 commits into
mainfrom
feat/dynamic-options-dropdowns
Jun 2, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 20 commits into
mainfrom
feat/dynamic-options-dropdowns

Conversation

@kvaps

@kvaps Andrei Kvapil (kvaps) commented Jun 1, 2026 •

Copy link
Copy Markdown
Member

What this PR does

Adds a generic mechanism for runtime-populated dropdowns in dashboard create/edit forms. Many fields are references to live cluster resources (GPU device names, KubeVirt instancetypes/preferences, Multus networks, VM images, bucket storage pools, storage classes, backup classes/plans) but were free-text or static enums that drift from reality.

  • Option resource — core.cozystack.io/v1alpha1, namespaced, read-only (list/get/watch). Items are computed on read by an in-process provider registry using a privileged client, so tenants get curated option lists without broad cluster reads. Providers: gpu, instancetype, instanceprofile, network, image, storagepool, storageclass, and backup-related backupclass, plan, backup, appkind.
  • x-cozystack-options schema keyword — app values.yaml declare it via the new cozyvalues-gen @x-<keyword> directive (e.g. ## @x-cozystack-options {source: instancetype}). Schemas, Go types, and the -rd ApplicationDefinitions are regenerated from source so the extension is carried into the served OpenAPI. The dashboard renders annotated fields as dropdowns sourced from the live cluster (UI widget ships in a separate cozystack-ui PR).
  • RBAC — the apiserver ServiceAccount gets the privileged reads; tenants get read-only access to options in their namespace via the aggregated tenant ClusterRoles (granted at cozy:tenant:view:base, so every access level from view upward can populate dropdowns).
  • Drops the redundant manual instanceProfile enum injection from vm-instance's Makefile (now driven dynamically).

Dependency on cozyvalues-gen

The x-cozystack-options annotations require the new vendor-extension directive from cozystack/cozyvalues-gen#27. This PR bumps the pinned cozyvalues-gen to v1.6.0. A cozyvalues-gen v1.6.0 release must be cut before CI can pass — until then the pre-commit schema-generation job cannot download the binary and will fail.

Screenshots

N/A — no UI changes in this repository. The consuming dashboard widget ships in a separate cozystack-ui PR.

Release note

feat(api): add a namespaced read-only Option resource (core.cozystack.io) and the x-cozystack-options schema keyword, enabling dashboard form fields (GPU, instancetype/profile, network, image, storage pool/class, backup class/plan) to render as dropdowns populated from live cluster resources

Summary by CodeRabbit

  • New Features

    • Expanded dynamic dropdowns across many apps (storageClass, storagePool, instanceType/instanceProfile, GPU, image, vmdisk, network, backup/plan/backupclass, appkind) so chart fields can be populated from live cluster resources.
  • Chores

    • Updated API/RBAC manifests to grant read access needed for option providers.
  • Tests

    • Added provider and REST tests to validate option sources, scoping, ordering, and failure handling.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a read-only core Option API and provider registry that compute dropdown option values on demand, implements REST storage (List/Get/Watch/Table), registers dynamic providers in the apiserver, updates RBAC, and annotates charts and CRDs with x-cozystack-options metadata linking fields to provider sources.

Changes

Dropdown Options API and Provider Infrastructure

Layer / File(s) Summary
Core Option API types and registration
pkg/apis/core/v1alpha1/option_types.go, pkg/apis/core/v1alpha1/register.go, pkg/apis/core/v1alpha1/zz_generated.deepcopy.go, pkg/apis/core/fuzzer/fuzzer.go, api/api-rules/cozystack_api_violation_exceptions.list
Option, OptionSpec, OptionItem, and OptionList types added with deepcopy methods, registered for the scheme, fuzzers switched to randfill, and API rule exception for OptionSpec.Items added.
Provider registry and implementations
pkg/registry/core/option/providers.go
Provider registry and providers for appkind, storageclass, vmdisk, gpu, image, storagepool, and generic name-list providers implemented with helper utilities.
Provider and REST tests
pkg/registry/core/option/providers_test.go
Extensive unit tests validate provider scoping, ordering, deduplication, GVR usage, and REST behavior including error handling.
REST storage and watch
pkg/registry/core/option/rest.go
Read-only REST storage implementing List/Get/Watch and table conversion; List tolerates provider failures, Get surfaces provider errors.
Apiserver wiring and RBAC
pkg/apiserver/apiserver.go, packages/system/cozystack-api/templates/rbac.yaml, packages/system/cozystack-basics/templates/clusterroles.yaml, packages/system/cozystack-api/tests/rbac_test.yaml
Dynamic client created during startup; Option REST registered with provider registry; RBAC rules expanded for cozystack-api and tenant roles; RBAC tests added.

Application Chart and CRD Schema Annotations

Layer / File(s) Summary
Storage class and pool annotations
packages/apps/*/values.schema.json, packages/apps/*/values.yaml (bucket, clickhouse, foundationdb, harbor, http-cache, kafka, mariadb, mongodb, nats, openbao, opensearch, postgres, qdrant, rabbitmq, redis, vm-disk)
Added x-cozystack-options metadata to storageClass/storagePool fields and corresponding @x-cozystack-options annotations in values.yaml across many charts.
Compute and VM resource annotations
packages/apps/kubernetes/*, packages/apps/vm-instance/*, packages/apps/vm-disk/*
Added x-cozystack-options for GPU/name, instanceType, instanceProfile, vmdisk, network, and image sources; vm-instance Makefile switched to generate schema via cozyvalues-gen and codegen helper.
Backup CRD schema annotations
packages/system/backup-controller/definitions/backups.cozystack.io_*.yaml
Added OpenAPI x-cozystack-options annotations for appkind, backupclass, plan, and backup fields in Backup, BackupJob, Plan, and RestoreJob CRDs.
ApplicationDefinition embedded schemas
packages/system/*/cozyrds/*.yaml
Embedded openAPISchema JSON updated to include x-cozystack-options metadata; some immutability validations adjusted/removed where shown.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

area/platform, area/testing

Suggested reviewers

  • lexfrei
  • androndo
  • sircthulhu
  • lllamnyp
  • IvanHunters
  • myasnikovdaniil

Poem

🐰 I nibble keys and hop through trees,

Providers hum and list with ease,
Storage, image, GPU in sight,
Dropdowns bloom in morning light—
Hooray, the options now take flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main feature: adding an Option resource and x-cozystack-options for dynamic form dropdowns in the API.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dynamic-options-dropdowns

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.

@github-actions github-actions Bot added area/api Issues or PRs related to the cozystack-api aggregated API server kind/feature Categorizes issue or PR as related to a new feature size/XXL This PR changes 1000+ lines, ignoring generated files labels Jun 1, 2026
@kvaps
Andrei Kvapil (kvaps) marked this pull request as ready for review June 1, 2026 23:07
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust mechanism for populating dashboard form dropdowns dynamically from live cluster state. By adding an Option resource and a custom OpenAPI schema extension, it replaces static or free-text fields with curated, real-time data. This change improves user experience by reducing configuration drift and simplifies tenant interactions by abstracting complex resource lookups into a standard, namespaced API.

Highlights

  • New Option Resource: Introduced a namespaced, read-only Option resource to provide dynamic, runtime-populated dropdown options for dashboard forms.
  • Schema Extension: Added the x-cozystack-options schema keyword, allowing values.yaml to declaratively source dropdown data from the cluster.
  • Dynamic Provider Registry: Implemented an in-process provider registry that computes option lists (e.g., GPU, network, storage class) on read, ensuring tenants access curated data without broad cluster permissions.
  • RBAC Updates: Updated the apiserver ServiceAccount with privileged read access and granted tenants read-only access to options via base ClusterRoles.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: **/zz_generated.*.go (2)
    • pkg/apis/core/v1alpha1/zz_generated.deepcopy.go
    • pkg/generated/openapi/zz_generated.openapi.go
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/pre-commit.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a new read-only virtual resource, Option, to dynamically serve dropdown choices (such as storage classes, instance types, and backup plans) for the dashboard UI. It implements the REST storage, dynamic providers, and registers the resource in the core API group, while updating various application schemas with x-cozystack-options annotations. The review feedback highlights a critical omission of RBAC permissions for listing plans, backups, and applicationdefinitions in the cozystack-api ServiceAccount, which would cause runtime failures. Additionally, it is recommended to use standard Kubernetes API markers (// +listType=map and // +listMapKey=value) on OptionSpec.Items to eliminate the need for an API rule violation exception.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +46 to +51
- apiGroups: ["backups.cozystack.io"]
resources: ["backupclasses"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps.cozystack.io"]
resources: ["vmdisks"]
verbs: ["get", "watch", "list"]

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.

high

The cozystack-api ServiceAccount is missing RBAC permissions to list plans and backups in the backups.cozystack.io API group, and applicationdefinitions in the apps.cozystack.io API group. Without these permissions, the plan, backup, and appkind option providers will fail with a Forbidden error at runtime when attempting to list these resources.

- apiGroups: ["backups.cozystack.io"]
  resources: ["backupclasses", "plans", "backups"]
  verbs: ["get", "watch", "list"]
- apiGroups: ["apps.cozystack.io"]
  resources: ["vmdisks", "applicationdefinitions"]
  verbs: ["get", "watch", "list"]

Comment on lines +20 to +23
// OptionSpec holds the computed option items for a source.
type OptionSpec struct {
Items []OptionItem `json:"items,omitempty"`
}

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.

low

Instead of adding an exception to api/api-rules/cozystack_api_violation_exceptions.list for list_type_missing on OptionSpec.Items, you can use standard Kubernetes API markers // +listType=map and // +listMapKey=value directly on the struct field. This is cleaner, adheres to Kubernetes API conventions, and allows you to remove the exception from the violation list.

Suggested change
// OptionSpec holds the computed option items for a source.
type OptionSpec struct {
Items []OptionItem `json:"items,omitempty"`
}
// OptionSpec holds the computed option items for a source.
type OptionSpec struct {
// +listType=map
// +listMapKey=value
Items []OptionItem `json:"items,omitempty"`
}

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
pkg/registry/core/option/providers.go (1)

56-71: Consider short-TTL caching for provider results.

Every read of an Option recomputes its source by issuing live dynamic List calls (e.g. gpuProvider lists KubeVirt + all Nodes, imageProvider lists PVCs, storageClassProvider lists all StorageClasses). Since the dashboard polls these dropdowns on each form open/edit and via Watch re-list, concurrent tenants can generate noticeable read load on the apiserver. A small bounded TTL cache (or shared informer/lister) per source would cut redundant cluster reads without changing the read-only contract.

🤖 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 `@pkg/registry/core/option/providers.go` around lines 56 - 71, The provider
functions (e.g., gpuProvider, imageProvider, storageClassProvider,
nameListProvider/nameListNamespacedProvider used in DefaultProviders) currently
call the dynamic client on every Option read; wrap these providers with a small
TTL cache or replace their internals with a shared informer/lister to avoid
repeated dynamic.List calls. Concretely, add a generic caching wrapper (e.g.,
cacheProvider(providerFunc, ttl)) and use it in DefaultProviders to return
cached results for a short duration (safely guarded by a mutex and invalidated
after ttl or on explicit watch events), or alternatively implement per-source
informer/lister usage inside the specific providers (gpuProvider, imageProvider,
storageClassProvider, vmDiskProvider, etc.) so they serve from a lister instead
of re-listing on every invocation.
pkg/apiserver/apiserver.go (1)

199-219: Heads-up: provider reads are uncached and privileged.

The dynamic client backs every Option list/get with direct, un-cached API reads of the underlying source kinds (nodes, KubeVirt, storageclasses, …). If the dashboard polls these dropdowns frequently, this could add measurable load on the kube-apiserver. Consider a short-TTL cache or informer-backed reads for the hottest sources if list latency/QPS becomes a concern in production.

🤖 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 `@pkg/apiserver/apiserver.go` around lines 199 - 219, The dynamic client
created by dynamic.NewForConfig (dyn) is used by optionstorage.DefaultProviders
and wired into optionstorage.NewREST, which performs uncached, privileged
list/get calls for provider-backed Option resources; to mitigate load, wrap or
replace the direct dynamic-backed providers with a caching layer or
informer-backed reads for hot sources (e.g., implement a short-TTL in-memory
cache or use informer listers for Node, KubeVirt, StorageClass kinds) and update
optionstorage.DefaultProviders to accept the cached/provider-lister
implementation instead of raw dyn so optionstorage.NewREST uses the cached
provider reads.
🤖 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 @.github/workflows/pre-commit.yml:
- Line 50: The workflow currently pipes the release tarball directly into tar,
which skips integrity checks; instead modify the Install generate step to
download the release to a temporary file (use variables like VERSION and ASSET),
compute/verify its SHA256 against a pinned EXPECTED_SHA256 (use TMPDIR for the
temp location), and only after a successful checksum run tar to extract
cozyvalues-gen into /usr/local/bin; ensure the step uses strict shell options
(set -euo pipefail) and fails the job if the checksum does not match.

In `@packages/system/cozystack-api/templates/rbac.yaml`:
- Around line 27-51: The RBAC block in templates/rbac.yaml misses some read-only
GVRs used by the Option dropdown providers: add "plans" and "backups" to the
apiGroup "backups.cozystack.io" and add "applicationdefinitions" to the apiGroup
"apps.cozystack.io" so the providers referenced in
pkg/registry/core/option/providers.go (the plan, backup and appkind option
providers) can list/get/watch those resources; keep verbs ["get","watch","list"]
for these entries to match the other read-only providers.

---

Nitpick comments:
In `@pkg/apiserver/apiserver.go`:
- Around line 199-219: The dynamic client created by dynamic.NewForConfig (dyn)
is used by optionstorage.DefaultProviders and wired into optionstorage.NewREST,
which performs uncached, privileged list/get calls for provider-backed Option
resources; to mitigate load, wrap or replace the direct dynamic-backed providers
with a caching layer or informer-backed reads for hot sources (e.g., implement a
short-TTL in-memory cache or use informer listers for Node, KubeVirt,
StorageClass kinds) and update optionstorage.DefaultProviders to accept the
cached/provider-lister implementation instead of raw dyn so
optionstorage.NewREST uses the cached provider reads.

In `@pkg/registry/core/option/providers.go`:
- Around line 56-71: The provider functions (e.g., gpuProvider, imageProvider,
storageClassProvider, nameListProvider/nameListNamespacedProvider used in
DefaultProviders) currently call the dynamic client on every Option read; wrap
these providers with a small TTL cache or replace their internals with a shared
informer/lister to avoid repeated dynamic.List calls. Concretely, add a generic
caching wrapper (e.g., cacheProvider(providerFunc, ttl)) and use it in
DefaultProviders to return cached results for a short duration (safely guarded
by a mutex and invalidated after ttl or on explicit watch events), or
alternatively implement per-source informer/lister usage inside the specific
providers (gpuProvider, imageProvider, storageClassProvider, vmDiskProvider,
etc.) so they serve from a lister instead of re-listing on every invocation.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eca96040-70be-4c0d-b1b0-2eeac5a18879

📥 Commits

Reviewing files that changed from the base of the PR and between d2bc261 and a687fe4.

⛔ Files ignored due to path filters (1)
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**
📒 Files selected for processing (71)
  • .github/workflows/pre-commit.yml
  • api/api-rules/cozystack_api_violation_exceptions.list
  • packages/apps/bucket/values.schema.json
  • packages/apps/bucket/values.yaml
  • packages/apps/clickhouse/values.schema.json
  • packages/apps/clickhouse/values.yaml
  • packages/apps/foundationdb/values.schema.json
  • packages/apps/foundationdb/values.yaml
  • packages/apps/harbor/values.schema.json
  • packages/apps/harbor/values.yaml
  • packages/apps/http-cache/values.schema.json
  • packages/apps/http-cache/values.yaml
  • packages/apps/kafka/values.schema.json
  • packages/apps/kafka/values.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/apps/kubernetes/values.yaml
  • packages/apps/mariadb/values.schema.json
  • packages/apps/mariadb/values.yaml
  • packages/apps/mongodb/values.schema.json
  • packages/apps/mongodb/values.yaml
  • packages/apps/nats/values.schema.json
  • packages/apps/nats/values.yaml
  • packages/apps/openbao/values.schema.json
  • packages/apps/openbao/values.yaml
  • packages/apps/opensearch/values.schema.json
  • packages/apps/opensearch/values.yaml
  • packages/apps/postgres/values.schema.json
  • packages/apps/postgres/values.yaml
  • packages/apps/qdrant/values.schema.json
  • packages/apps/qdrant/values.yaml
  • packages/apps/rabbitmq/values.schema.json
  • packages/apps/rabbitmq/values.yaml
  • packages/apps/redis/values.schema.json
  • packages/apps/redis/values.yaml
  • packages/apps/vm-disk/values.schema.json
  • packages/apps/vm-disk/values.yaml
  • packages/apps/vm-instance/Makefile
  • packages/apps/vm-instance/values.schema.json
  • packages/apps/vm-instance/values.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/bucket-rd/cozyrds/bucket.yaml
  • packages/system/clickhouse-rd/cozyrds/clickhouse.yaml
  • packages/system/cozystack-api/templates/rbac.yaml
  • packages/system/cozystack-basics/templates/clusterroles.yaml
  • packages/system/foundationdb-rd/cozyrds/foundationdb.yaml
  • packages/system/harbor-rd/cozyrds/harbor.yaml
  • packages/system/http-cache-rd/cozyrds/http-cache.yaml
  • packages/system/kafka-rd/cozyrds/kafka.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • packages/system/mariadb-rd/cozyrds/mariadb.yaml
  • packages/system/mongodb-rd/cozyrds/mongodb.yaml
  • packages/system/nats-rd/cozyrds/nats.yaml
  • packages/system/openbao-rd/cozyrds/openbao.yaml
  • packages/system/opensearch-rd/cozyrds/opensearch.yaml
  • packages/system/postgres-rd/cozyrds/postgres.yaml
  • packages/system/qdrant-rd/cozyrds/qdrant.yaml
  • packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml
  • packages/system/redis-rd/cozyrds/redis.yaml
  • packages/system/vm-disk-rd/cozyrds/vm-disk.yaml
  • packages/system/vm-instance-rd/cozyrds/vm-instance.yaml
  • pkg/apis/core/fuzzer/fuzzer.go
  • pkg/apis/core/v1alpha1/option_types.go
  • pkg/apis/core/v1alpha1/register.go
  • pkg/apis/core/v1alpha1/zz_generated.deepcopy.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/core/option/providers.go
  • pkg/registry/core/option/providers_test.go
  • pkg/registry/core/option/rest.go
💤 Files with no reviewable changes (1)
  • packages/apps/vm-instance/Makefile

- name: Install generate
run: |
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.5.0/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.6.0/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== pre-commit workflow around line 50 ==="
if [ -f .github/workflows/pre-commit.yml ]; then
  nl -ba .github/workflows/pre-commit.yml | sed -n '35,75p'
else
  echo "Missing: .github/workflows/pre-commit.yml"
fi

echo
echo "=== find cozyvalues-gen in repo ==="
rg -n "cozyvalues-gen" -S . || true

echo
echo "=== inspect referenced Makefile section if present ==="
if [ -f packages/apps/vm-instance/Makefile ]; then
  nl -ba packages/apps/vm-instance/Makefile | sed -n '1,60p'
else
  echo "Missing: packages/apps/vm-instance/Makefile"
fi

echo
echo "=== check for checksum/install hardening patterns in workflow ==="
rg -n "sha256|gpg|cosign|sums\.txt|checksums|verify" .github/workflows/pre-commit.yml -S || true

Repository: cozystack/cozystack

Length of output: 167


🌐 Web query:

cozystack cozyvalues-gen v1.6.0 checksums.txt release asset name

💡 Result:

I can’t find any authoritative source stating that cozystack/cozyvalues-gen has a v1.6.0 release asset named checksums.txt. What I found instead: - The cozystack/cozyvalues-gen repository exists, but its latest release shown is v1.3.0 (not v1.6.0) on GitHub Releases pages. [1] - Searches for “v1.6.0” and “checksums.txt” within the cozyvalues-gen release assets did not return a matching release asset. If you meant a different repo/component (e.g., cozy/cozy-stack rather than cozyvalues-gen), or if the version tag is formatted differently (for example, v1.6.0 vs 1.6.0), tell me and I’ll re-check. [1] https://github.com/cozystack/cozyvalues-gen

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== pre-commit workflow around line 50 ==="
if [ -f .github/workflows/pre-commit.yml ]; then
  awk 'NR>=35 && NR<=80 {printf "%5d:%s\n", NR, $0}' .github/workflows/pre-commit.yml
else
  echo "Missing: .github/workflows/pre-commit.yml"
fi

echo
echo "=== find cozyvalues-gen in repo ==="
rg -n "cozyvalues-gen" -S . || true

echo
echo "=== check for checksum/signature verification patterns in workflow ==="
rg -n "sha256|gpg|cosign|sums\.txt|checksums|verify" .github/workflows/pre-commit.yml -S || true

echo
echo "=== search all workflows for integrity/verify patterns near cozyvalues-gen ==="
rg -n "cozyvalues-gen.*(sha256|gpg|cosign|verify|checksums|sum)" -S .github/workflows || true

Repository: cozystack/cozystack

Length of output: 8648


Verify cozyvalues-gen release artifact integrity before extracting binary.

In .github/workflows/pre-commit.yml (Install generate, line 50), CI pipes a GitHub release tarball directly into tar and installs cozyvalues-gen into /usr/local/bin with no checksum/signature verification—this is a supply-chain risk.

🔐 Suggested hardening
set -euo pipefail
VERSION="v1.6.0"
ASSET="cozyvalues-gen-linux-amd64.tar.gz"

TMPDIR="$(mktemp -d)"
curl -fsSL "https://github.com/cozystack/cozyvalues-gen/releases/download/${VERSION}/${ASSET}" -o "${TMPDIR}/${ASSET}"

# Replace with the known SHA256 for that exact asset version.
echo "${EXPECTED_SHA256}  ${TMPDIR}/${ASSET}" | sha256sum -c -

tar -xzf "${TMPDIR}/${ASSET}" -C /usr/local/bin
🤖 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 @.github/workflows/pre-commit.yml at line 50, The workflow currently pipes
the release tarball directly into tar, which skips integrity checks; instead
modify the Install generate step to download the release to a temporary file
(use variables like VERSION and ASSET), compute/verify its SHA256 against a
pinned EXPECTED_SHA256 (use TMPDIR for the temp location), and only after a
successful checksum run tar to extract cozyvalues-gen into /usr/local/bin;
ensure the step uses strict shell options (set -euo pipefail) and fails the job
if the checksum does not match.

Comment on lines +27 to +51
# Read-only sources for the Option (dropdown) resource providers.
- apiGroups: [""]
resources: ["nodes", "persistentvolumeclaims"]
verbs: ["get", "watch", "list"]
- apiGroups: ["kubevirt.io"]
resources: ["kubevirts"]
verbs: ["get", "watch", "list"]
- apiGroups: ["instancetype.kubevirt.io"]
resources: ["virtualmachineclusterinstancetypes", "virtualmachineclusterpreferences"]
verbs: ["get", "watch", "list"]
- apiGroups: ["k8s.cni.cncf.io"]
resources: ["network-attachment-definitions"]
verbs: ["get", "watch", "list"]
- apiGroups: ["objectstorage.k8s.io"]
resources: ["bucketclasses"]
verbs: ["get", "watch", "list"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "watch", "list"]
- apiGroups: ["backups.cozystack.io"]
resources: ["backupclasses"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps.cozystack.io"]
resources: ["vmdisks"]
verbs: ["get", "watch", "list"]

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate the option providers and extract the GVRs they read, to compare with RBAC rules.
fd providers.go | rg 'core/option' || fd providers.go

f=$(fd providers.go | rg 'core/option' | head -n1)
echo "=== provider file: $f ==="
# Show GroupVersionResource definitions / Group/Resource literals
rg -nP '(GroupVersionResource|Group|Resource|Version)\s*:\s*"' "$f"
# Show any group/resource string constants used for dynamic Resource() calls
rg -nP 'schema\.GroupVersionResource|\.Resource\(' "$f" -A2 -B2

Repository: cozystack/cozystack

Length of output: 5984


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/system/cozystack-api/templates/rbac.yaml"

echo "=== File: $file (lines 1-120) ==="
nl -ba "$file" | sed -n '1,120p'

echo
echo "=== Search RBAC for option provider GVR resources ==="
rg -n "plans|backups\b|applicationdefinitions|backupclasses|helmreleases|vmdisks|gvr" "$file" || true

echo
echo "=== Show any rules block that might cover dynamic option providers (around lines 20-70) ==="
nl -ba "$file" | sed -n '20,90p'

Repository: cozystack/cozystack

Length of output: 202


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/system/cozystack-api/templates/rbac.yaml"

echo "=== wc -l $file ==="
wc -l "$file"

echo
echo "=== Show $file line 1-160 with line numbers ==="
awk '{printf "%6d %s\n", NR, $0}' "$file" | sed -n '1,160p'

echo
echo "=== Search RBAC template for option-provider related resources ==="
rg -n --fixed-strings "plans" "$file" || true
rg -n --fixed-strings "backups" "$file" || true
rg -n --fixed-strings "applicationdefinitions" "$file" || true
rg -n --fixed-strings "backupclasses" "$file" || true
rg -n --fixed-strings "vmdisks" "$file" || true
rg -n "helmreleases|nodes|persistentvolumeclaims|kubevirts|virtualmachineclusterinstancetypes|virtualmachineclusterpreferences|network-attachment-definitions|storageclasses" "$file" || true

Repository: cozystack/cozystack

Length of output: 2914


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="pkg/registry/core/option/providers.go"

echo "=== Relevant GVR definitions ==="
sed -n '30,70p' "$file" | nl -ba

echo
echo "=== Where dyn.Resource(...).List is called for plans/backups/app defs/helm releases ==="
rg -n "gvrPlans|gvrBackups|gvrAppDefs|gvrHelmReleases" "$file"

echo
echo "=== Show the provider functions around those usages ==="
for line in $(rg -n "gvrPlans|gvrBackups|gvrAppDefs|gvrHelmReleases" "$file" | cut -d: -f1 | sort -n | uniq); do
  start=$((line-15)); end=$((line+25));
  echo "---- $file:$start-$end (around line $line) ----"
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {print NR ":" $0}' "$file" | sed -n '1,120p'
done

Repository: cozystack/cozystack

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Locate providers.go ==="
fd -a "providers.go" -t f . | head -n 20

echo
echo "=== Use pkg/registry/core/option/providers.go if it exists ==="
file="pkg/registry/core/option/providers.go"
if [ -f "$file" ]; then
  echo "Found: $file"
else
  echo "Missing: $file"
  exit 1
fi

echo
echo "=== Relevant GVR definitions ==="
awk 'NR>=30 && NR<=70 {print NR ":" $0}' "$file"

echo
echo "=== Where dyn.Resource(...).List is called for plans/backups/app defs/helm releases ==="
rg -n "gvrPlans|gvrBackups|gvrAppDefs|gvrHelmReleases" "$file"

echo
echo "=== Show provider functions around those usages ==="
lines=$(rg -n "gvrPlans|gvrBackups|gvrAppDefs|gvrHelmReleases" "$file" | cut -d: -f1 | sort -n | uniq)
for line in $lines; do
  start=$((line-20)); end=$((line+20));
  echo
  echo "---- $file:$start-$end (around line $line) ----"
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {print NR ":" $0}' "$file" | sed -n '1,180p'
done

Repository: cozystack/cozystack

Length of output: 23766


Fix RBAC read permissions for Option dropdown providers (missing GVRs).

  • packages/system/cozystack-api/templates/rbac.yaml grants backups.cozystack.io read-only access only for backupclasses, but pkg/registry/core/option/providers.go also lists backups.cozystack.io/v1alpha1 plans and backups for the plan and backup option providers.
  • The same RBAC block grants apps.cozystack.io read-only access only for vmdisks, but the appkind option provider lists apps.cozystack.io/v1alpha1 applicationdefinitions.
🤖 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 `@packages/system/cozystack-api/templates/rbac.yaml` around lines 27 - 51, The
RBAC block in templates/rbac.yaml misses some read-only GVRs used by the Option
dropdown providers: add "plans" and "backups" to the apiGroup
"backups.cozystack.io" and add "applicationdefinitions" to the apiGroup
"apps.cozystack.io" so the providers referenced in
pkg/registry/core/option/providers.go (the plan, backup and appkind option
providers) can list/get/watch those resources; keep verbs ["get","watch","list"]
for these entries to match the other read-only providers.

Andrei Kvapil (kvaps) and others added 11 commits June 2, 2026 16:32
The roundtrip test for the core API group panicked (Filler.Funcs:
customFuncs' second argument must be a randfill.Continue) because the
fuzzer still used github.com/google/gofuzz while apimachinery switched
to sigs.k8s.io/randfill. Port the fuzz functions to randfill.Continue
/ FillNoCustom.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
Introduce a read-only, namespaced virtual resource
core.cozystack.io/v1alpha1 Option that serves named lists of dropdown
options for the dashboard. Each object (one per source: gpu,
instancetype, instanceprofile, network, image, storagepool,
storageclass, vmdisk, backupclass) is computed on read by an in-process
provider using a privileged dynamic client, so tenants need no direct
access to Nodes, the KubeVirt CR, cluster instancetypes, etc.

Tenants get read access via the aggregated cozy:tenant roles; the
apiserver ServiceAccount gains read on the underlying sources.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
Mark fields that reference cluster resources with the x-cozystack-options
vendor keyword so the dashboard renders them as dynamic dropdowns:
GPU, instanceType, instanceProfile, network (vm-instance), GPU/
instanceType (kubernetes nodeGroups), image (vm-disk), storagePool
(bucket), storageClass (all apps) and disks[].name (vm-instance). Drop
the stale static instanceProfile enum.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
Add three dropdown sources to the Option provider registry so the
dashboard's backup forms can use the unified x-cozystack-options
mechanism instead of injecting client-side enum arrays:

- plan / backup: namespaced, list Plans / Backups in the tenant namespace
- appkind: cluster-scoped, lists application kinds from ApplicationDefinitions

Annotate the backups.cozystack.io CRD schemas (Plan, BackupJob, Backup,
RestoreJob) with x-cozystack-options on backupClassName (backupclass),
applicationRef.kind / targetApplicationRef.kind (appkind), planRef.name
(plan) and backupRef.name (backup). This also gives the previously
unused backupclass source its first consumer.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
Move the dynamic-dropdown x-cozystack-options vendor extensions from the
generated values.schema.json into the values.yaml source for vm-instance,
kubernetes, vm-disk and bucket so they survive schema regeneration. The new
cozyvalues-gen @x-<keyword> directive emits these extensions from the source
annotations. Annotations on typedef @field entries (Disk, Network, GPU,
NodeGroup) are declared once and cover every place the typedef is reused.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
…es.yaml

Add the storageclass dynamic-dropdown x-cozystack-options annotation to the
storageClass field of the data apps (openbao, clickhouse, mariadb, redis,
harbor, opensearch, rabbitmq, mongodb, postgres, nats, kafka, qdrant,
foundationdb, http-cache) at the values.yaml source, so the extension is
emitted by cozyvalues-gen instead of hand-edited into values.schema.json.
kafka carries two (Kafka and ZooKeeper structs); foundationdb is nested
under the Storage struct.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
The instanceProfile field is now driven by an x-cozystack-options dynamic
dropdown (source: instanceprofile), so the Makefile generate step no longer
needs to post-inject a static enum into values.schema.json via yq. Remove
that injection along with the already-commented-out instanceType enum
injection so the dynamic dropdown is not fought by a stale static enum.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
Options were granted on cozy:tenant:use:base, leaving view-only tenant
users (cozy:tenant:view) without read access — so their create/edit
forms could not populate dynamic dropdowns. Move the grant down to
cozy:tenant:view:base, which aggregates up into use/admin/super-admin,
covering every human tenant access level. The tenant ServiceAccount role
keeps its grant via cozy:tenant:base.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
…ystack-options

Regenerate values.schema.json and embedded openAPISchema in the -rd
ApplicationDefinitions so the x-cozystack-options dynamic-source
directives declared in each app values.yaml are carried into the
generated artifacts. Brings the -rd schemas in sync with the
hand-edited values.schema.json files (CI runs make generate +
git diff --exit-code).

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
… group

The appkind option provider queried applicationdefinitions in the
apps.cozystack.io group, but the ApplicationDefinition CRD is served from
cozystack.io. The provider listed a resource that does not exist, so the
appkind source resolved empty on every cluster (tolerated and dropped by
List, 500 on Get), and the backup form fields that reference it never
populated.

Point the provider at cozystack.io, which the existing cozystack.io read
grant already covers. Add a guard test pinning the CRD group, since the
existing fake-client test is built from the same GVR and cannot catch a
wrong group.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
The plan and backup option providers list namespaced plans and backups in
the backups.cozystack.io group, but the cozystack-api ClusterRole only
granted backupclasses there. The apiserver's privileged client runs as this
ServiceAccount, so both providers got forbidden — List silently dropped the
sources and Get returned 500, leaving the plan/backup dropdowns in the
backup forms empty.

Add plans and backups to the backups.cozystack.io rule and cover the grant
with a chart test.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
@lexfrei
Aleksei Sviridkin (lexfrei) force-pushed the feat/dynamic-options-dropdowns branch from eb275f1 to 4cbe5b6 Compare June 2, 2026 13:44

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/system/cozystack-basics/templates/clusterroles.yaml (1)

42-48: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep options out of cozy:tenant:base.

This widens a privileged virtual resource from view-role users to every tenant ServiceAccount. The same read rule is already added in cozy:tenant:view:base at Lines 123-127, which matches the stated dashboard dropdown use case without exposing option listings to workloads.

Suggested RBAC trim
 - apiGroups:
   - core.cozystack.io
   resources:
   - tenantmodules
   - tenantsecrets
-  - options
   verbs: ["get", "list", "watch"]
🤖 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 `@packages/system/cozystack-basics/templates/clusterroles.yaml` around lines 42
- 48, Remove the "options" resource from the cozy:tenant:base ClusterRole's
rules so tenant workloads don't get read access; locate the cluster role
definition that lists apiGroups: core.cozystack.io with resources tenantmodules,
tenantsecrets, options and delete "options" from that resources array, leaving
the verbs unchanged, while leaving the existing "options" read rule in the
cozy:tenant:view:base ClusterRole intact (it already grants the dashboard view
use case).
🤖 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.

Outside diff comments:
In `@packages/system/cozystack-basics/templates/clusterroles.yaml`:
- Around line 42-48: Remove the "options" resource from the cozy:tenant:base
ClusterRole's rules so tenant workloads don't get read access; locate the
cluster role definition that lists apiGroups: core.cozystack.io with resources
tenantmodules, tenantsecrets, options and delete "options" from that resources
array, leaving the verbs unchanged, while leaving the existing "options" read
rule in the cozy:tenant:view:base ClusterRole intact (it already grants the
dashboard view use case).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c01a5fcb-f98d-43d3-abc9-62d58ae57b43

📥 Commits

Reviewing files that changed from the base of the PR and between eb275f1 and 4cbe5b6.

⛔ Files ignored due to path filters (1)
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**
📒 Files selected for processing (71)
  • api/api-rules/cozystack_api_violation_exceptions.list
  • packages/apps/bucket/values.schema.json
  • packages/apps/bucket/values.yaml
  • packages/apps/clickhouse/values.schema.json
  • packages/apps/clickhouse/values.yaml
  • packages/apps/foundationdb/values.schema.json
  • packages/apps/foundationdb/values.yaml
  • packages/apps/harbor/values.schema.json
  • packages/apps/harbor/values.yaml
  • packages/apps/http-cache/values.schema.json
  • packages/apps/http-cache/values.yaml
  • packages/apps/kafka/values.schema.json
  • packages/apps/kafka/values.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/apps/kubernetes/values.yaml
  • packages/apps/mariadb/values.schema.json
  • packages/apps/mariadb/values.yaml
  • packages/apps/mongodb/values.schema.json
  • packages/apps/mongodb/values.yaml
  • packages/apps/nats/values.schema.json
  • packages/apps/nats/values.yaml
  • packages/apps/openbao/values.schema.json
  • packages/apps/openbao/values.yaml
  • packages/apps/opensearch/values.schema.json
  • packages/apps/opensearch/values.yaml
  • packages/apps/postgres/values.schema.json
  • packages/apps/postgres/values.yaml
  • packages/apps/qdrant/values.schema.json
  • packages/apps/qdrant/values.yaml
  • packages/apps/rabbitmq/values.schema.json
  • packages/apps/rabbitmq/values.yaml
  • packages/apps/redis/values.schema.json
  • packages/apps/redis/values.yaml
  • packages/apps/vm-disk/values.schema.json
  • packages/apps/vm-disk/values.yaml
  • packages/apps/vm-instance/Makefile
  • packages/apps/vm-instance/values.schema.json
  • packages/apps/vm-instance/values.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/bucket-rd/cozyrds/bucket.yaml
  • packages/system/clickhouse-rd/cozyrds/clickhouse.yaml
  • packages/system/cozystack-api/templates/rbac.yaml
  • packages/system/cozystack-api/tests/rbac_test.yaml
  • packages/system/cozystack-basics/templates/clusterroles.yaml
  • packages/system/foundationdb-rd/cozyrds/foundationdb.yaml
  • packages/system/harbor-rd/cozyrds/harbor.yaml
  • packages/system/http-cache-rd/cozyrds/http-cache.yaml
  • packages/system/kafka-rd/cozyrds/kafka.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • packages/system/mariadb-rd/cozyrds/mariadb.yaml
  • packages/system/mongodb-rd/cozyrds/mongodb.yaml
  • packages/system/nats-rd/cozyrds/nats.yaml
  • packages/system/openbao-rd/cozyrds/openbao.yaml
  • packages/system/opensearch-rd/cozyrds/opensearch.yaml
  • packages/system/postgres-rd/cozyrds/postgres.yaml
  • packages/system/qdrant-rd/cozyrds/qdrant.yaml
  • packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml
  • packages/system/redis-rd/cozyrds/redis.yaml
  • packages/system/vm-disk-rd/cozyrds/vm-disk.yaml
  • packages/system/vm-instance-rd/cozyrds/vm-instance.yaml
  • pkg/apis/core/fuzzer/fuzzer.go
  • pkg/apis/core/v1alpha1/option_types.go
  • pkg/apis/core/v1alpha1/register.go
  • pkg/apis/core/v1alpha1/zz_generated.deepcopy.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/core/option/providers.go
  • pkg/registry/core/option/providers_test.go
  • pkg/registry/core/option/rest.go
💤 Files with no reviewable changes (1)
  • packages/apps/vm-instance/Makefile
✅ Files skipped from review due to trivial changes (21)
  • packages/apps/http-cache/values.yaml
  • packages/system/harbor-rd/cozyrds/harbor.yaml
  • packages/apps/qdrant/values.yaml
  • packages/apps/kafka/values.yaml
  • packages/apps/rabbitmq/values.yaml
  • packages/system/kafka-rd/cozyrds/kafka.yaml
  • packages/apps/postgres/values.yaml
  • packages/apps/opensearch/values.yaml
  • packages/apps/openbao/values.yaml
  • packages/apps/mariadb/values.yaml
  • packages/apps/redis/values.yaml
  • packages/apps/qdrant/values.schema.json
  • packages/apps/bucket/values.yaml
  • packages/apps/nats/values.yaml
  • packages/apps/foundationdb/values.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml
  • packages/apps/mongodb/values.schema.json
  • packages/apps/opensearch/values.schema.json
  • packages/apps/kubernetes/values.schema.json
  • pkg/apis/core/v1alpha1/zz_generated.deepcopy.go
  • packages/apps/vm-disk/values.yaml
🚧 Files skipped from review as they are similar to previous changes (43)
  • packages/apps/clickhouse/values.yaml
  • packages/apps/mongodb/values.yaml
  • packages/apps/postgres/values.schema.json
  • packages/apps/clickhouse/values.schema.json
  • packages/apps/mariadb/values.schema.json
  • api/api-rules/cozystack_api_violation_exceptions.list
  • packages/system/postgres-rd/cozyrds/postgres.yaml
  • packages/apps/rabbitmq/values.schema.json
  • packages/system/qdrant-rd/cozyrds/qdrant.yaml
  • packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml
  • packages/apps/http-cache/values.schema.json
  • packages/system/mongodb-rd/cozyrds/mongodb.yaml
  • packages/system/nats-rd/cozyrds/nats.yaml
  • packages/apps/vm-disk/values.schema.json
  • packages/system/mariadb-rd/cozyrds/mariadb.yaml
  • packages/apps/bucket/values.schema.json
  • packages/system/vm-disk-rd/cozyrds/vm-disk.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml
  • packages/system/foundationdb-rd/cozyrds/foundationdb.yaml
  • packages/system/redis-rd/cozyrds/redis.yaml
  • packages/apps/nats/values.schema.json
  • packages/apps/kafka/values.schema.json
  • pkg/apiserver/apiserver.go
  • packages/apps/openbao/values.schema.json
  • pkg/apis/core/v1alpha1/register.go
  • packages/system/clickhouse-rd/cozyrds/clickhouse.yaml
  • pkg/apis/core/v1alpha1/option_types.go
  • packages/apps/harbor/values.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • packages/system/openbao-rd/cozyrds/openbao.yaml
  • packages/system/opensearch-rd/cozyrds/opensearch.yaml
  • packages/apps/vm-instance/values.yaml
  • packages/system/cozystack-api/tests/rbac_test.yaml
  • pkg/registry/core/option/providers_test.go
  • packages/system/vm-instance-rd/cozyrds/vm-instance.yaml
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/cozystack-api/templates/rbac.yaml
  • packages/apps/kubernetes/values.yaml
  • packages/apps/foundationdb/values.schema.json
  • pkg/registry/core/option/rest.go
  • packages/apps/redis/values.schema.json
  • pkg/registry/core/option/providers.go

The Option providers RBAC block granted read on
objectstorage.k8s.io/bucketclasses, but no provider in the registry lists
BucketClasses and no schema field references a bucketclass source — the
bucket app's storage pool field uses the storagepool provider, which reads
seaweedfs HelmReleases. Drop the unused grant and pin its absence in the
chart test.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
…n provider error

The Option REST deliberately makes List tolerate a single failing provider
(it logs and skips it, so a partial RBAC config drops one dropdown instead
of breaking the whole list) while Get surfaces the error as a 500. Neither
path was covered. Add tests that inject a failing provider and assert List
keeps the healthy sources and omits the failing one, and that Get returns an
InternalError.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
…ntroller-gen

controller-gen cannot emit the x-cozystack-options vendor extension, so the
annotations on the backup CRDs (backupjobs/backups/plans/restorejobs) were
hand-edited into generated output — the next make generate would strip them,
breaking the appkind/plan/backup form dropdowns and failing the codegen-drift
check.

Add a deterministic awk pass that re-applies the extension after
controller-gen, driven by per-CRD field targets. It inserts lines without
reserializing the YAML, so it reproduces the committed CRDs byte-for-byte and
is independent of the host yq version.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
fuzzer.go imports sigs.k8s.io/randfill directly after the gofuzz-to-randfill
migration, but go.mod still listed it under the indirect block. go mod tidy
moves it into the direct require block; commit the result so the metadata
matches the import and a contributor running go mod tidy gets no diff.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
…test passes

The core fuzzer was migrated from gofuzz to randfill, but the apps fuzzer was
left on gofuzz. TestRoundTripTypes runs both fuzzers (apps first), so
registering a gofuzz.Continue func against the randfill-based roundtrip harness
panics ("customFuncs' second argument must be a randfill.Continue").

Migrate the apps fuzzer the same way (randfill.Continue / FillNoCustom). With
no gofuzz consumers left, go mod tidy drops the dependency. TestRoundTripTypes
now passes.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
The gpu, image, storagepool, storageclass and vmdisk providers carry the real
parsing logic (whitelist-intersect-allocatable, prefix strip, nested pool
walk, default-class marking, size label) but had no tests — and List tolerates
a provider error by skipping it, so a logic bug ships as a silently empty or
wrong dropdown rather than a failure. Add table tests against the fake dynamic
client for each, raising option-package coverage from 39% to 77%.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
…tions

The tenant view aggregation grants read on the core.cozystack.io/options
virtual resource so dashboard dropdowns can be populated. Add a helm-unittest
on the cozy:tenant:view:base ClusterRole so a silent RBAC regression on the
tenant side is caught, mirroring the apiserver-side rbac test.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
The one-shot option watch emitted the current state once then blocked on
ctx.Done(). If the initial List failed (e.g. every provider forbidden by a
broken RBAC config) it blocked silently, leaving the client with an empty
stream indistinguishable from "no options". Log the error so an operator can
tell the two apart.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
instancetype/instanceprofile replaced the static vm-instance enum with live
dropdowns sourced from instancetype.kubevirt.io. A wrong group/version would
make those dropdowns silently empty in every cluster (List tolerates the
error) — the failure mode the appkind GVR guard already catches. Pin both
GVRs to the served API.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/registry/core/option/providers_test.go (1)

195-200: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert NotFound for unknown option source
The test only fails on err == nil, so a regression returning a different error type would slip through; check apierrors.IsNotFound(err) instead.

Suggested tightening
 func TestRESTGetUnknownSourceNotFound(t *testing.T) {
 	dyn := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), listKinds())
 	r := NewREST(DefaultProviders(dyn))
 	if _, err := r.Get(context.Background(), "does-not-exist", &metav1.GetOptions{}); err == nil {
 		t.Fatal("expected NotFound for unknown source")
+	} else if !apierrors.IsNotFound(err) {
+		t.Fatalf("expected NotFound for unknown source, got %v", err)
 	}
 }
🤖 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 `@pkg/registry/core/option/providers_test.go` around lines 195 - 200, The test
TestRESTGetUnknownSourceNotFound should verify the error is a NotFound error
instead of only checking for non-nil; update the assertion after calling
r.Get(...) to use apierrors.IsNotFound(err) (importing
k8s.io/apimachinery/pkg/api/errors as apierrors) so the test fails if a
different error type is returned; keep the rest of the test and the same setup
using NewREST and DefaultProviders(dyn).
🤖 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 `@pkg/registry/core/option/providers_test.go`:
- Around line 355-357: The test currently calls
DefaultProviders(dyn)["storagepool"](context.Background(), "") and only asserts
len(empty)==0 while ignoring the returned error; update the assertion to also
capture and assert that the error is nil so failures where the provider returns
an unexpected error are caught. Locate the call to DefaultProviders and the
"storagepool" provider invocation in providers_test.go and change the test to
check both the slice length is zero and that the error value returned is nil
(i.e., fail the test if err != nil).

---

Outside diff comments:
In `@pkg/registry/core/option/providers_test.go`:
- Around line 195-200: The test TestRESTGetUnknownSourceNotFound should verify
the error is a NotFound error instead of only checking for non-nil; update the
assertion after calling r.Get(...) to use apierrors.IsNotFound(err) (importing
k8s.io/apimachinery/pkg/api/errors as apierrors) so the test fails if a
different error type is returned; keep the rest of the test and the same setup
using NewREST and DefaultProviders(dyn).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ec94e353-2f5d-43d9-9add-f5b22c599663

📥 Commits

Reviewing files that changed from the base of the PR and between 4cbe5b6 and c9655e2.

📒 Files selected for processing (9)
  • go.mod
  • hack/inject-cozystack-options.awk
  • hack/update-codegen.sh
  • packages/system/cozystack-api/templates/rbac.yaml
  • packages/system/cozystack-api/tests/rbac_test.yaml
  • packages/system/cozystack-basics/tests/clusterroles-options_test.yaml
  • pkg/apis/apps/fuzzer/fuzzer.go
  • pkg/registry/core/option/providers_test.go
  • pkg/registry/core/option/rest.go
💤 Files with no reviewable changes (1)
  • packages/system/cozystack-api/templates/rbac.yaml
✅ Files skipped from review due to trivial changes (1)
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/system/cozystack-api/tests/rbac_test.yaml
  • pkg/registry/core/option/rest.go

Comment on lines +355 to +357
// Namespaced provider opts out without a namespace.
if empty, _ := DefaultProviders(dyn)["storagepool"](context.Background(), ""); len(empty) != 0 {
t.Fatalf("storagepool: expected no items without a namespace")

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t ignore the error on the empty-namespace branch.

If storagepool starts returning an error instead of cleanly opting out, this assertion still passes because it only checks len(empty). That weakens the contract this test is trying to pin.

Suggested tightening
 	// Namespaced provider opts out without a namespace.
-	if empty, _ := DefaultProviders(dyn)["storagepool"](context.Background(), ""); len(empty) != 0 {
+	empty, err := DefaultProviders(dyn)["storagepool"](context.Background(), "")
+	if err != nil {
+		t.Fatalf("storagepool provider empty ns: %v", err)
+	}
+	if len(empty) != 0 {
 		t.Fatalf("storagepool: expected no items without a namespace")
 	}
📝 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
// Namespaced provider opts out without a namespace.
if empty, _ := DefaultProviders(dyn)["storagepool"](context.Background(), ""); len(empty) != 0 {
t.Fatalf("storagepool: expected no items without a namespace")
// Namespaced provider opts out without a namespace.
empty, err := DefaultProviders(dyn)["storagepool"](context.Background(), "")
if err != nil {
t.Fatalf("storagepool provider empty ns: %v", err)
}
if len(empty) != 0 {
t.Fatalf("storagepool: expected no items without a namespace")
}
🤖 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 `@pkg/registry/core/option/providers_test.go` around lines 355 - 357, The test
currently calls DefaultProviders(dyn)["storagepool"](context.Background(), "")
and only asserts len(empty)==0 while ignoring the returned error; update the
assertion to also capture and assert that the error is nil so failures where the
provider returns an unexpected error are caught. Locate the call to
DefaultProviders and the "storagepool" provider invocation in providers_test.go
and change the test to check both the slice length is zero and that the error
value returned is nil (i.e., fail the test if err != nil).

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.

LGTM. Read-only Option resource (core.cozystack.io) computing dropdown items from live cluster state via privileged apiserver-side providers, plus the x-cozystack-options schema keyword carried from app values into the served OpenAPI. The design keeps tenants off direct reads on Nodes/KubeVirt/instancetypes while scoping namespaced sources to the request namespace — no cross-tenant leak on an empty-namespace list.

Verified: build/vet/lint clean; provider and REST logic are tested (whitelist-intersect-allocatable, prefix strip, default-class marking, pool/zone walk, namespaced scoping, List-tolerate vs Get-500); RBAC grants match every provider GVR; the generation chain reproduces (codegen-drift green), including the backup-CRD x-cozystack-options re-injection.

Fixes I pushed on top:

  • appkind provider queried the wrong API group (apps.cozystack.io -> cozystack.io) — it was silently empty in every cluster.
  • RBAC was missing plans/backups in backups.cozystack.io — added.
  • removed an unused objectstorage/bucketclasses grant.
  • backup-CRD x-cozystack-options were hand-edited into generated files; added a deterministic codegen step so make generate reproduces them (codegen-drift would otherwise fail).
  • finished the gofuzz->randfill migration (the apps fuzzer was left behind and panicked the roundtrip test); marked randfill as a direct dependency.
  • added provider/REST test coverage and GVR guard tests.

Non-blocking: the PR body's "bumps cozyvalues-gen to v1.6.0" line is stale — that bump already landed on main, there is no version diff here; and the option watch logs an all-providers-failed case at V(2), worth raising to a warning so a total RBAC failure is not quiet.

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

Labels

area/api Issues or PRs related to the cozystack-api aggregated API server kind/feature Categorizes issue or PR as related to a new feature size/XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants