Skip to content

feat(computeplane): provision a ComputePlane tenant module for untrusted-code apps - #3280

Merged
Andrei Kvapil (kvaps) merged 14 commits into
mainfrom
feat/compute-plane-module
Jul 29, 2026
Merged

feat(computeplane): provision a ComputePlane tenant module for untrusted-code apps#3280
Andrei Kvapil (kvaps) merged 14 commits into
mainfrom
feat/compute-plane-module

Conversation

@kvaps

@kvaps Andrei Kvapil (kvaps) commented Jul 13, 2026

Copy link
Copy Markdown
Member

What this PR does

Implements the ComputePlane per the merged design proposal (cozystack/community design-proposals/compute-plane; supersedes the earlier #3150 which was built on the pre-revision surface and came from a fork): a managed, isolated environment for running code-executing (untrusted-code) catalog applications.

ComputePlane is delivered as an operator-owned tenant module:

  • packages/extra/computeplane — the module chart. Provisions a single-tenant, Cozystack-managed cluster by deploying the ordinary apps/kubernetes chart (Kamaji control plane + KubeVirt-VM workers) with operator-fixed values: the Cozystack-enablement addons (fluxcd + pinned distribution artifact, certManager, ingressNginx with exposeMethod: Proxied) are inline spec.values, so nothing injected via valuesFrom can disable them. The tenant can set cluster shape (nodeGroups, schema-validated) and receives no admin kubeconfig.
  • Release naming: neither HelmRelease sets spec.releaseName — the aggregated API rebuilds HelmRelease specs without that field, so both releases derive names from their object names. The cluster HelmRelease object is computeplane-cluster, and Kamaji writes the admin kubeconfig to the derived Secret computeplane-cluster-admin-kubeconfig — the contract future placement: ComputePlane routing consumes. Tenant-visible secrets are an allowlist (include: [] withholds everything), so the credential is withheld by construction.
  • packages/system/computeplane-rd — registers the ComputePlane module-kind (ApplicationDefinition, dashboard.module: true).
  • packages/core/platform/sources/computeplane-application.yaml — self-contained package source: re-declares apps/kubernetes as its own source-only component; dependsOn cozystack-engine (ApplicationDefinition CRD) and kubernetes-application (registration ordering). Ships in the iaas bundle (variant kubevirt).
  • Tenant togglecomputeplane: false bool. The tenant chart fails loudly when the module is toggled on without the iaas package. Note: automatic placement: ComputePlane routing is a deferred follow-up and is not part of this PR; until it lands, external catalogs target the cluster via its kubeconfig Secret.
  • apps/kubernetes fix — the Proxied ingress Service renders only when the ingress-nginx addon is enabled and the tenant has etcd (mirroring the addon HelmRelease gates), independent of the hosts list.

Follow-ups tracked separately: placement routing in cozystack-api, scoped per-service egress, visibility/mutation access control, e2e (#3369), release-name guards for the remaining extra charts (#3371).

Tests: helm-unittest suites pin the module contract (object names, no explicit releaseName, tamper-proof inline addons, nodeGroups validation + ingress-role guard, unlisted-field pass-through), the tenant toggle, the package-source/bundle wiring, and the ingress Service gates.

Screenshots

Not a UI change.

Release note

feat(computeplane): ComputePlane tenant module — a single-tenant, Cozystack-managed cluster for untrusted-code applications, delivered as an operator-owned extra module wrapping the kubernetes app. Enable with `computeplane: true` on the Tenant; the tenant receives no admin kubeconfig for it, and the cluster's admin kubeconfig Secret is `computeplane-cluster-admin-kubeconfig`. Catalog routing (`placement: ComputePlane`) ships separately.

Summary by CodeRabbit

  • New Features
    • Added a new ComputePlane tenant option (computeplane, default off) to provision an isolated single-tenant environment for untrusted-code applications, including cluster creation and configurable node groups.
  • Bug Fixes
    • Refined ingress-nginx proxied rendering rules to avoid emitting incomplete resources: Ingress/Services are now gated on addon enablement (and etcd-backed tenancy), with correct behavior when hosts aren’t provided.
  • Documentation / Tests
    • Updated tenant and ComputePlane documentation; added/extended rendering tests for the new ComputePlane module and ingress-nginx gating (including release-name and etcd-less cases).

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ComputePlane package sources, charts, application definitions, tenant configuration and HelmRelease wiring, configurable cluster node groups, release-name validation, and ingress-nginx gating with expanded Helm tests.

Changes

ComputePlane platform and tenant integration

Layer / File(s) Summary
Platform package source and IaaS wiring
packages/core/platform/sources/computeplane-application.yaml, packages/core/platform/templates/bundles/iaas.yaml, packages/core/platform/tests/sources_computeplane_test.yaml
Adds the KubeVirt ComputePlane package source, components, IaaS bundle wiring, and integration coverage.
ComputePlane cluster chart
packages/extra/computeplane/*
Adds chart metadata, node-group schema and documentation, a cluster HelmRelease, addon configuration, ingress-role validation, canonical release-name enforcement, and Helm tests.
ComputePlane application definition
packages/system/computeplane-rd/*
Adds the computeplane-rd chart and embeds an ApplicationDefinition with node-group schema, Flux chart wiring, and tenant resource exposure settings.
Tenant ComputePlane configuration
api/apps/v1alpha1/tenant/types.go, packages/apps/tenant/*, packages/system/tenant-rd/cozyrds/tenant.yaml
Adds the default-disabled computeplane option, tenant schema and documentation, conditional HelmRelease rendering with package guards, and tenant schema ordering metadata.
Proxied ingress resource gating
packages/apps/kubernetes/templates/ingress.yaml, packages/apps/kubernetes/tests/ingress_proxied_gate_test.yaml
Gates proxied ingress resources on addon enablement and etcd availability, renders the Service without requiring hosts, and tests disabled, hostless, hosted, and etcd-less cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TenantConfig
  participant TenantModule
  participant ComputeplanePackage
  participant ComputeplaneCluster
  TenantConfig->>TenantModule: set computeplane=true
  TenantModule->>ComputeplanePackage: reference computeplane application artifact
  ComputeplanePackage->>ComputeplaneCluster: create computeplane-cluster HelmRelease
  ComputeplaneCluster->>ComputeplaneCluster: apply addons and nodeGroups values
Loading

Possibly related issues

Suggested labels: kind/api-change, area/kubernetes

Suggested reviewers: lexfrei

🚥 Pre-merge checks | ✅ 5
✅ 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 clearly describes the main change: provisioning a ComputePlane tenant module for untrusted-code applications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/compute-plane-module

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.

@github-actions github-actions Bot added area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/feature Categorizes issue or PR as related to a new feature size/XL This PR changes 500-999 lines, ignoring generated files labels Jul 13, 2026
Andrei Kvapil (kvaps) added a commit to cozystack/community that referenced this pull request Jul 16, 2026
The illustrative HelmRelease conflated the tenant-rendered module release
with the cluster release the module renders, making extra/computeplane look
bypassed. Show both levels — tenant chart → extra/computeplane (release
computeplane-module) → re-sourced apps/kubernetes (release computeplane →
the computeplane-admin-kubeconfig contract) — matching the implementation
in cozystack/cozystack#3280, and pin the tenant toggle as the computeplane
bool.

Assisted-By: Claude
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
namespace: cozy-system
path: /
variants:
- name: kubevirt

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this kubevirt?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Documented in the file now: the variant mirrors the kubernetes-application variant whose chart it re-sources — the wrapped substrate is the kubevirt flavour of apps/kubernetes, and the artifact naming contract (cozystack-computeplane-application-kubevirt-*) carries that provenance. A future non-kubevirt substrate would arrive as a sibling variant.

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking on process first: this cannot merge until cozystack/community#33 is approved and merged. The proposal is approved but still open, and this PR implements its surface directly — the module contract (release name, kubeconfig secret, tenant-visible knobs) is exactly what the proposal fixes. Merging the implementation while the design can still move means any late revision lands as a breaking change to an already-shipped module kind. Please land #33 first, then rebase this.

The module itself is in good shape. Wrapping apps/kubernetes with operator-fixed inline spec.values is the right lever — Flux merges spec.values after valuesFrom, so the addon pins genuinely cannot be disabled through cozystack-values. Reserving the canonical release name for the cluster HelmRelease so Kamaji writes computeplane-admin-kubeconfig, and excluding that Secret from tenant-visible secrets, is a clean way to hold the credential. The source-only re-declaration of apps/kubernetes is safe as built: the package reconciler skips components without an install: block, so the duplicate component yields an artifact and never a colliding HelmRelease. And the cozystack-engine edge is correct — it really does gate the computeplane-rd release that would otherwise fail to render against a missing ApplicationDefinition CRD.

Findings below, ranked.


1. packages/apps/kubernetes/templates/ingress.yaml — the Proxied Service now renders on every cluster, including those with no ingress-nginx

The guard went from and (eq exposeMethod "Proxied") .hosts to if eq exposeMethod "Proxied", with a new end closing the hosts condition above the Service. That's the intended change for ComputePlane, but it drops the only thing that was standing in for "the operator actually wants ingress here": exposeMethod defaults to Proxied while addons.ingressNginx.enabled defaults to false.

Rendering the chart against its own tests/values-ci.yaml (which configures no ingressNginx at all) emits nothing before this change and emits <release>-ingress-nginx after it. So on upgrade, every existing kind: Kubernetes application gains a Service in its tenant namespace whose selector (node-role.kubernetes.io/ingress-nginx: "") matches no pods. It is not invisible either — packages/system/kubernetes-rd/cozyrds/kubernetes.yaml lists kubernetes-{{ .name }}-ingress-nginx under services.include, so it surfaces in the tenant dashboard advertising ports 80/443 that route nowhere.

Suggested fix — gate on the addon rather than on hosts, which still satisfies ComputePlane since it sets enabled: true explicitly:

{{- if and .Values.addons.ingressNginx.enabled (eq .Values.addons.ingressNginx.exposeMethod "Proxied") }}

2. packages/core/platform/templates/bundles/iaas.yaml — the toggle ships in system, the backing package ships in iaas

Every other tenant module toggle (etcd, monitoring, ingress, gateway, seaweedfs) is backed by a package in the system bundle, so the toggle always resolves. ComputePlane is the first whose package lives in iaas, while the computeplane field itself arrives via tenant-rd in the system bundle. Bundles are independent booleans with no cross-validation.

Failure: on a system + paas install, a tenant sets computeplane: true and gets a HelmRelease pointing at a cozystack-computeplane-application-kubevirt-computeplane artifact that does not exist. With install.remediation.retries: -1 it retries forever, with no diagnostic and no dashboard entry (the ComputePlane kind is not registered either, since computeplane-rd is in the same absent package). Either move the package to the system bundle, or have the tenant chart fail loudly when the module is toggled on without its package.

3. packages/extra/computeplane/values.schema.jsonnodeGroups is unvalidated, and can defeat the pinned ingress addon

The module declares nodeGroups as a bare type: object with x-kubernetes-preserve-unknown-fields: true. The wrapped kubernetes app validates the same knob against a typed schema (diskSize, gpus, instanceType, kubelet, maxReplicas, minReplicas, resources, roles, storageClass) with per-field enums and patterns. Two consequences:

  • A bad instanceType or a misspelled field draws no admission error. It lands in the cluster HelmRelease and fails deep in the wrapped chart's render, surfacing as a stuck release rather than a rejected write.
  • More importantly: user-supplied nodeGroups are authoritative and are not merged with the default md0 (per the kubernetes.nodeGroups helper), and the ingress-nginx controller carries nodeSelector: node-role.kubernetes.io/ingress-nginx: "". A tenant who supplies any nodeGroups without roles: [ingress-nginx] leaves the controller Pending indefinitely and the ComputePlane's ingress never comes up.

The README states "the security posture is fixed by the module and cannot be overridden", but ingress availability is in practice reachable through the one knob the module does expose. Reusing the kubernetes app's nodeGroups schema addresses the validation half; the roles requirement wants either defaulting in the module or an explicit warning in the parameter docs.

4. packages/core/platform/sources/computeplane-application.yaml — the comment overstates what the kubernetes-application edge does

The comment explains the edge as needed "because the wrapped apps/kubernetes chart renders child HelmReleases whose chartRefs point at that package's addon artifacts". Variant dependsOn only gates HelmRelease creation for components carrying an install: block — here that is computeplane-rd alone. The cluster HelmRelease is rendered by the tenant chart and is not ordered by this edge, and the addon artifacts it references are produced unconditionally by the PackageSource reconciler regardless of dependency readiness. The edge is harmless and the artifact naming is right; the stated reason just isn't the operative one.


Checked and fine

  • Re-running hack/update-crd.sh in packages/extra/computeplane reproduces the committed ApplicationDefinition byte-for-byte, so the generated RD is in sync with the generator.
  • The releaseName: computeplane-module / object-name computeplane split does not break application lookup, which keys off the HelmRelease object name rather than spec.releaseName.
  • The victoria-metrics-operator gate that other tenant modules carry is correctly absent — apps/kubernetes renders no VM* resources on the management side.
  • All touched helm-unittest suites pass.

Not blocking

No e2e coverage is added. Standing up a full Kamaji + KubeVirt cluster in CI is a real cost and the unit suites do pin the module contract meaningfully, so this reads as a reasonable follow-up rather than a merge condition — but it is worth an issue so it does not get lost behind the placement work.

@kvaps
Andrei Kvapil (kvaps) force-pushed the feat/compute-plane-module branch from 177927e to 5cde79f Compare July 20, 2026 11:54
@kvaps

Copy link
Copy Markdown
Member Author

All four findings addressed, and rebased on main now that cozystack/community#33 is merged:

  1. Proxied Service now gates on addons.ingressNginx.enabled (7e65f73) — plus a regression test covering disabled/no-hosts/hosts renders.
  2. Bundle mismatch: the tenant chart now fails loudly (b573540) when computeplane: true is set without the cozystack.computeplane-application Package present, pointing at the iaas bundle. Moving the package into the system bundle instead would wedge system-only installs: its dependsOn: cozystack.kubernetes-application would keep the Package DependenciesNotReady forever and block platform readiness — the same trap the velero test guards against.
  3. nodeGroups now reuses the kubernetes app's typedefs for a fully typed schema, and the render refuses nodeGroups where no group carries roles: [ingress-nginx] (1ee9e4c), with tests for both directions.
  4. dependsOn comment rewritten to state the operative effect — ordering the computeplane-rd install so the ComputePlane kind is not registered before the kubernetes machinery exists (5cde79f).

The e2e follow-up is filed separately so it does not get lost behind the placement work.

@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 implements the ComputePlane module, which provides a secure, isolated Kubernetes cluster environment for running untrusted code. By wrapping the standard Kubernetes application with operator-fixed values, it ensures that security-critical addons are always enabled while withholding admin credentials from the tenant. The changes include new module definitions, updated tenant configuration schemas, and improvements to the ingress proxying logic to support external management access.

Highlights

  • ComputePlane Module Implementation: Introduced the ComputePlane tenant module, providing a managed, isolated environment for running untrusted code applications.
  • Tenant Configuration: Added a new 'computeplane' boolean toggle to the Tenant configuration, allowing users to enable the module easily.
  • Ingress Logic Improvement: Updated the Kubernetes application ingress template to create a proxy service whenever 'exposeMethod' is set to 'Proxied', regardless of the hosts list.
  • Package Integration: Created a new package source for 'computeplane-application' and integrated it into the 'iaas' bundle to ensure proper deployment and dependency management.
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.

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.

@dosubot dosubot Bot added the area/extra Issues or PRs related to tenant-specific modules (packages/extra/) label Jul 20, 2026

@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 the 'ComputePlane' feature, a single-tenant, Cozystack-managed cluster for running untrusted-code applications. It adds the computeplane configuration option to the tenant API, creates the computeplane extra package and its resource definitions, wires it into the IaaS bundle, and updates the ingress configuration in the kubernetes application to support proxied routing. The review feedback suggests improving Helm template safety in ingress.yaml by using a nil-safe field walking pattern to prevent potential nil pointer dereference panics, and pinning the Flux operator manifests artifact in cluster.yaml to a specific stable version instead of using the :latest tag to ensure reproducible deployments.

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 +2 to +3
{{- if and .Values.addons.ingressNginx.enabled (eq .Values.addons.ingressNginx.exposeMethod "Proxied") }}
{{- if .Values.addons.ingressNginx.hosts }}

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

To prevent potential nil pointer dereference panics when addons or ingressNginx is omitted or null in custom values, use the repository's established parenthesized nil-safe field walking pattern (e.g., ((( .Values.addons ).ingressNginx).enabled)). This ensures that nested field accesses safely evaluate to nil (which is falsy) rather than causing a template rendering error.

{{- if and (((.Values.addons).ingressNginx).enabled) (eq (((.Values.addons).ingressNginx).exposeMethod) "Proxied") }}
{{- if (((.Values.addons).ingressNginx).hosts) }}

flux-instance:
instance:
distribution:
artifact: "oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests:latest"

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.

medium

Using the :latest tag for the Flux operator manifests can lead to non-reproducible deployments and unexpected breaking changes when upstream updates their manifests. It is highly recommended to pin this to a specific stable version (e.g., v0.14.0 or another tested release) to ensure stability and predictability in production environments.

                artifact: "oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests:v0.14.0"

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All four findings from the previous round are addressed on their merits, with tests, and community#33 has merged — the process block is cleared. The addon gate on the Proxied Service is exactly right, the corrected dependsOn comment now describes what variant dependencies actually do, and the nodeGroups typedefs are a real improvement. Requesting changes on one blocking issue plus a few smaller items.

Blocking: the release-name divergence is unrepresentable in the aggregated API

packages/apps/tenant/templates/computeplane.yaml sets spec.releaseName: computeplane-module, and packages/extra/computeplane/templates/cluster.yaml pins spec.releaseName: computeplane on the inner cluster HelmRelease. That split cannot survive a write through the aggregated API.

computeplane-rd declares release.prefix: "", so a ComputePlane object named computeplane maps onto the same HelmRelease the tenant chart renders. ApplicationDefinitionRelease has exactly three fields — ChartRef, Labels, Prefix — with no way to express a release name, and convertApplicationToHelmRelease never assigns Spec.ReleaseName. REST.Update replaces the live spec with that rebuilt one, carrying over only labels and the shard key, and Flux does not pin releaseName, so the field is silently dropped.

The first edit through kubectl or the dashboard therefore blanks releaseName, it defaults to the object name computeplane, and that is the exact Helm release computeplane-cluster owns — two HelmReleases driving one Helm storage record with two different charts. The tenant chart then reconciles at its 5m interval and writes computeplane-module back, so the name oscillates. Blast radius is the Kamaji control plane, the worker MachineDeployments and the admin kubeconfig Secret.

This is on the main path rather than a corner: nodeGroups is the module's only tenant-facing knob, and the aggregated API is the only way to set it, since the tenant chart's module HelmRelease passes valuesFrom and no values: at all.

Why this module and not the others

Worth spelling out, because the fix depends on it. Every other tenant module that renders a nested HelmRelease derives the inner name from the release and lets the inner release name default to the object name: external-dns and monitoring use {{ .Release.Name }}-system, seaweedfs uses {{ .Release.Name }}-db. None of them sets spec.releaseName, and computeplane.yaml is the only template under packages/apps/tenant/templates/ that sets it at all.

computeplane inverts the pattern: it hardcodes the inner object name and claims the canonical release name for the inner release, which pushes the outer module release off onto a suffixed one. It does that for a real reason — Kamaji derives the computeplane-admin-kubeconfig Secret from the Helm release name, so the inner release name is load-bearing for an external contract. No other module has a downstream consumer that reads its inner release name, which is why suffixing the inner one is free for all of them and not for this one.

So the general shape the aggregated API can express is exactly one release name per object, prefix + name. Any module whose outer release name diverges from that is unrepresentable — and on closer look the divergence buys nothing, because the inner release name is only load-bearing in the sense that something downstream has to know it, not that it has to be any particular string.

Fix: leave both releaseName fields blank, as every other module does

Drop spec.releaseName from both HelmReleases and let Flux derive them. Flux defaults releaseName to [TargetNamespace-]Name, and neither HelmRelease sets targetNamespace, so the outer module release becomes computeplane from its object name and the inner cluster release becomes computeplane-cluster from its own. Nothing collides, nothing diverges, and the aggregated API round-trips cleanly because prefix + name is then exactly what both already are — an Update that rebuilds the spec without releaseName reproduces the same release name rather than changing it.

This also puts the module back on the same pattern as external-dns, monitoring and seaweedfs, which is worth something on its own.

The consequence is that Kamaji writes the admin kubeconfig to computeplane-cluster-admin-kubeconfig instead of computeplane-admin-kubeconfig, which is fine — nothing consumes it yet, since placement: ComputePlane is a deferred follow-up — but it must be changed everywhere in the same commit.

The security-critical one is packages/system/computeplane-rd/cozyrds/computeplane.yaml, whose secrets.exclude withholds the credential by literal name under resourceNames. Rename the release without updating that entry and the exclusion silently stops matching, so the cluster admin kubeconfig becomes visible to the tenant — precisely the credential the whole design exists to withhold. The remaining references are prose and test comments: packages/extra/computeplane/templates/cluster.yaml, packages/extra/computeplane/README.md, packages/apps/tenant/templates/computeplane.yaml, packages/extra/computeplane/tests/cluster_test.yaml and packages/apps/tenant/tests/computeplane_module_test.yaml.

Since the merged proposal names computeplane-admin-kubeconfig when describing the deferred placement injection, that line wants a follow-up amendment so the future kubeConfig.secretRef is written against the derived name.

This wants two tests: a helm-unittest case asserting the tenant module HelmRelease carries no spec.releaseName, and one asserting the excluded secret name in computeplane-rd matches the name the inner release actually produces — the second is the one that would catch a future rename re-exposing the credential.

Add the release-name guard, and file the gap for the rest

packages/extra/computeplane has no check-release-name.yaml. Four of the nine extra charts ship one — bootbox, etcd, ingress, monitoring — so it is an established pattern rather than a new demand, and it is cheap here. It also fails closed on the issue above: a dropped releaseName yields computeplane, which the guard rejects loudly instead of silently colliding two releases.

The other four charts without a guard (external-dns, gateway, info, seaweedfs) have the same latent exposure and are out of scope for this PR — worth a tracking issue rather than scope creep here. Note that community#39 proposes cardinality as a declarative capability on ApplicationDefinition, which would replace all of these hand-rolled per-chart guards; the issue should reference it so the guards are understood as a stopgap.

Close the nodeGroup object in the schema

The typedefs landed, but values.schema.json still has no additionalProperties: false, so unlisted nodeGroup keys pass through to the wrapped chart unvalidated — maxUnhealthy and nodeStartupTimeout among them. For a module whose security argument is that the chart is the profile, the README's "only the cluster shape the module exposes" is not yet true.

Close it through the generator by adjusting the NodeGroup typedef in values.yaml and re-running make -C packages/extra/computeplane generate, rather than hand-editing the schema or the embedded openAPISchema. If the generator cannot emit additionalProperties: false, drop the claim from the README and values.yaml and state plainly that unlisted fields pass through. A helm-unittest case setting an unlisted nodeGroup field and asserting rejection pins whichever way it goes.

Two notes, not change requests

The etcd dependency is a precedent worth recording, not a defect. A ComputePlane with no tenant etcd gets no cluster-autoscaler, so the default nodeGroups: {} scale-from-zero md0 never receives its trigger and the cluster provisions no workers. That is not specific to the pinned addons — none of the tenant Kubernetes machinery works without etcd, and that is the intended design. What is new is that this makes one tenant module depend on another, which the module system has no way to express or surface today. A tenant enabling computeplane without etcd gets silence rather than a diagnostic. Worth deciding deliberately whether to document the prerequisite in the module README and the tenant README, or to surface it, and worth capturing the module-depends-on-module precedent somewhere durable — plausibly as input to community#39, which is already reshaping how modules declare themselves.

The package guard's blast radius is a machinery-wide deficiency. Helm's fail aborts the whole chart, so a tenant flipping computeplane: true on an install without the iaas bundle freezes reconciliation of everything else the tenant chart owns — namespace labels, quotas, network policy, ingress, monitoring — behind remediation.retries: -1. The guard itself is correct and the mechanism checks out; the problem is that a tenant-settable boolean can wedge the tenant's entire configuration surface, and that is true of any fail in the tenant chart rather than anything this PR introduced. community#39's move away from the special-cased extra bucket is the right place to fix the class. No change requested here beyond awareness.

Smaller

packages/apps/kubernetes/tests/ingress_proxied_gate_test.yaml covers enabled/disabled and hosts/no-hosts, but not enabled: false with exposeMethod: Proxied and a non-empty hosts list. That combination used to render an Ingress and now renders nothing. It is almost certainly the intended reading of "the addon is off", but it is a real behaviour change for any existing cluster in that state — worth a case pinning it deliberately so it reads as a decision.

packages/system/computeplane-rd/values.yaml contains a literal {}; the sibling seaweedfs-rd ships no values.yaml at all. Drop it for consistency.

@kvaps

Copy link
Copy Markdown
Member Author

The blocking issue is fixed as prescribed, plus the smaller items:

  • Release names (ec22f5b): both HelmReleases drop spec.releaseName and derive from object names, like every other tenant module. Kamaji now writes computeplane-cluster-admin-kubeconfig; the computeplane-rd secrets.exclude entry moved in the same commit, and two new tests pin it — a helm-unittest asserting the module HR carries no releaseName, and hack/check-computeplane-kubeconfig-exclusion.bats asserting the excluded Secret name matches what the cluster release actually produces (renders the chart and derives the name, so a future rename fails there instead of re-exposing the credential). The check-release-name guard is added with tests, and the guard gap in the other four extra charts is filed as fix(extra): add the check-release-name guard to external-dns, gateway, info and seaweedfs #3371 referencing community#39's cardinality. The proposal amendment for the derived Secret name is docs(compute-plane): derive release names — the kubeconfig Secret is computeplane-cluster-admin-kubeconfig community#40.
  • Schema (ecd8eeb): the generator cannot emit additionalProperties: false (the kubernetes app's own NodeGroup schema is open too), so per your fallback the README and the nodeGroups description now state plainly that unlisted fields pass through unvalidated, and a unittest pins the pass-through (flagged to flip to rejection if the generator ever learns to close objects).
  • etcd prerequisite: documented in the module README (no workers, no diagnostic without it). Agreed the module-depends-on-module precedent belongs in community#39's scope.
  • Ingress test (413df59): the disabled-addon-with-hosts case is pinned as a deliberate behaviour change.
  • Also pinned the fluxcd distribution.artifact to the flux-operator version vendored in system/fluxcd-operator instead of :latest (bot-flagged, and you get reproducible in-cluster Flux).
  • computeplane-rd/values.yaml: kept — seaweedfs-rd and etcd-rd both ship a literal {} values.yaml, so dropping ours would be the inconsistency.

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The release-name fix is right, and deriving both names from object names is the change that makes this representable through the aggregated API. check-release-name, the pinned flux-operator version instead of :latest, and the two pushbacks — the -rd values.yaml convention and the generator's inability to emit additionalProperties: false — are all correct; thanks for checking those rather than just applying them.

Most of what remains is drift between what the code does and what its comments, docs and PR body say. One item is functional, and one is a correction to something the previous round asked for.

Retracting the secrets.exclude framing — the previous round had the mechanism wrong

The last review called the computeplane-admin-kubeconfig entry security-critical and asked for a test pinning it to the derived release name. That was wrong, and the resulting hack/check-computeplane-kubeconfig-exclusion.bats should come back out.

matchResourceToExcludeInclude in internal/lineagecontrollerwebhook/matcher.go returns the value of matchResourceToSelectorArray(..., resources.Include), and that function returns false on an empty slice. With include: [], no secret in a ComputePlane's lineage is ever labelled internal.cozystack.io/tenantresource: "true", and pkg/registry/core/tenantsecret/rest.go surfaces only labelled secrets. It is an allowlist, not a denylist — the exclude branch never becomes load-bearing, so renaming the release could not have exposed the credential.

The clearest evidence is in this PR already: packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml consumes two Kamaji credentials, {{ .Release.Name }}-admin-kubeconfig and {{ .Release.Name }}-kubeconfig. The exclude list names only the first. If that list were the guard the comments claim, the second cluster-admin credential would already be exposed — it is not, because the empty include list withholds everything.

So the withholding is rename-proof by construction, which is a stronger property than the one the previous round asked to pin. The suggested resolution is to drop the resourceNames entry and keep exclude: [] / include: [] as seaweedfs-rd does, rewrite the three comment blocks that assert the rename coupling — in computeplane-rd/cozyrds/computeplane.yaml, extra/computeplane/templates/cluster.yaml and extra/computeplane/README.md — to state the real mechanism, and delete the bats file. Keeping the literal entry as defence-in-depth is also defensible, but then it should say so and should list both secrets, so it is not mistaken for the mechanism again.

No replacement static test. The property is "a tenant-scoped client cannot read the ComputePlane's kubeconfig Secrets", which lives between the lineage webhook, the aggregated API and RBAC at runtime; a file-comparison test cannot observe it and gives false assurance about the most important security claim in the PR. That belongs in the ComputePlane e2e already filed.

The Proxied Service escapes the etcd gate

packages/apps/kubernetes/templates/ingress.yaml gates the Service on and .Values.addons.ingressNginx.enabled (eq ... "Proxied"), but the ingress-nginx addon HelmRelease that backs it is gated on and .Values.addons.ingressNginx.enabled .Values._namespace.etcd. ComputePlane pins the addon on, so on an etcd-less tenant the chart emits computeplane-cluster-ingress-nginx with no controller behind it — confirmed by rendering packages/apps/kubernetes against tests/values-ci-no-etcd.yaml with the addon enabled and exposeMethod: Proxied.

That is the same selector-less-Service outcome the addon gate was added to prevent, reached by a different route, and it contradicts the comment now sitting above the Service. It also breaks the etcd-less contract the rest of the chart maintains, where the render collapses to the awaiting-etcd beacon and nothing else.

Adding .Values._namespace.etcd to the Service condition fixes it. A case in ingress_proxied_gate_test.yaml with the addon enabled and _namespace.etcd empty, asserting nothing renders, pins it — that one is a genuine render-behaviour assertion rather than a drift guard, so it is worth having.

The etcd prerequisite: two details wrong in the README

Both are worth correcting whether or not the guard becomes a hard fail.

"the tenant must have the etcd module enabled" is stricter than reality — packages/apps/tenant/templates/namespace.yaml inherits _namespace.etcd from the parent tenant, so an ancestor's etcd satisfies the wrapped chart, and a tenant following this literally provisions a redundant one. "gives no diagnostic" is also not quite right: packages/apps/kubernetes/templates/cluster.yaml emits the awaiting-etcd ConfigMap as a status beacon. It is a weak diagnostic — both HelmReleases still report Ready over an empty shell — but it exists and should be named.

Whether to escalate this from documentation to a render-time fail alongside the package guard is your call, and there is a reasonable argument either way now that the beacon is in the picture. If it stays documentation, the two corrections above are enough.

placement: ComputePlane is advertised but not shipped

The Tenant field description in api/apps/v1alpha1/tenant/types.go says "Applications with placement: ComputePlane are routed onto it." That field does not exist anywhere in Go, charts or CRDs — it is the deferred follow-up. The text is baked into the Tenant openAPISchema in tenant-rd, so it renders in the dashboard, and it is repeated in packages/apps/tenant/values.yaml, values.schema.json and README.md.

An operator enabling computeplane: true today therefore provisions a Kamaji control plane and KubeVirt workers that nothing can be scheduled onto, with the field description implying otherwise. Reword to state what ships now and note the routing is not yet available, then regenerate through make generate so all four copies follow rather than hand-editing them.

The larger question is whether the toggle should ship before its consumer exists at all. Gating it until the placement work lands would avoid advertising an inert capability, and is worth a deliberate decision rather than defaulting to shipping.

Drift

packages/extra/computeplane/templates/cluster.yaml still names the Service computeplane-ingress-nginx; after the release rename it renders as computeplane-cluster-ingress-nginx. The per-app management Ingress follow-up will be written against whatever this comment says, so it is worth fixing now.

The PR body still describes the pre-rename scheme — module release taking the -module suffix, canonical computeplane reserved for the cluster, Secret computeplane-admin-kubeconfig. The shipped code is the inverse. Since the release note is drafted from this text, it should be updated before merge.

None of the items in this section need tests; the codegen check already covers the generated copies.

Recommended, not blocking

The ingress-role guard requires some group to carry roles: [ingress-nginx] but not that the group can ever run a pod — a matching group with maxReplicas: 0 reproduces the Pending-forever failure the guard exists to prevent. Requiring maxReplicas >= 1 on the matching group would close it.

internal.cozystack.io/computeplane: "true" on the cluster HelmRelease has no reader anywhere in the tree. If it is a hook for the placement follow-up, a comment saying so would stop the next person removing it as dead.

The package guard fails closed on a nil lookup, where the comparable guard in the platform chart fails only when the lookup returns something. The Capabilities gate covers offline helm template, but a client-side render against a live cluster's discovery would trip it and fail the whole tenant render. Worth confirming no such path exists in the packaging pipeline.

Finally, worth confirming against the design that module self-provisioning is acceptable: with release.prefix: "", a tenant can create a ComputePlane object named computeplane through the aggregated API in a tenant where the operator left the toggle off, and check-release-name blocks every name except that one. This is true of every dashboard.module: true RD and is not introduced here — but ComputePlane is the first module where the unauthorised object is a VM-backed Kubernetes cluster, which changes the cost of the gap.

@kvaps

Copy link
Copy Markdown
Member Author

Round three addressed (d1dddd7):

  • Proxied Service now gates on _namespace.etcd like the addon HelmRelease it fronts, with the etcd-less render case pinned in ingress_proxied_gate_test.yaml.
  • Secrets framing corrected per the retraction: the exclude entry, the bats cross-check and the rename-coupling comments are gone; the three comment blocks now state the allowlist mechanism (include: [] withholds everything). Took your primary suggestion (exclude: [] / include: [] as seaweedfs-rd) rather than defence-in-depth. The runtime property stays with the e2e follow-up (test(e2e): cover the computeplane module end-to-end #3369).
  • etcd prerequisite rewritten: ancestor etcd satisfies the wrapped chart via _namespace.etcd inheritance, and the awaiting-etcd beacon is named as the (weak) diagnostic. Kept as documentation rather than a render-time fail — the beacon plus the docs seem proportionate, and a hard fail would break the legitimate enable-both-in-one-edit flow.
  • placement de-advertised: the toggle description now states routing is a deferred follow-up and external catalogs target the cluster via its kubeconfig Secret; regenerated through make generate so all four copies follow. On the larger question — shipping the toggle now is deliberate: the first consumer (cozyllm) targets the Secret directly ahead of the in-tree routing, and the module contract is what this PR pins.
  • Drift: the Service-name comment and the PR body/release note now describe the shipped derived-name scheme; the ingress-role guard requires maxReplicas >= 1 on the matching group; the internal.cozystack.io/computeplane label and the package-guard lookup semantics are commented.
  • Module self-provisioning: agreed it changes cost here; it is a property of every dashboard.module: true RD and belongs with community#39's protection capability — noted there rather than special-cased in this PR.

@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

🤖 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 `@packages/apps/tenant/README.md`:
- Line 85: The computeplane README entry contradicts itself about tenant
kubeconfig access. Update the row to state that
computeplane-cluster-admin-kubeconfig is platform-internal and unavailable to
tenant administrators, or remove the external-catalog workaround if the include
allowlist prevents its use; keep the no-admin-kubeconfig tenant behavior
explicit.
🪄 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: 494d8cd0-252f-4938-bb3c-f43e1c638f06

📥 Commits

Reviewing files that changed from the base of the PR and between 413df59 and d1dddd7.

📒 Files selected for processing (11)
  • api/apps/v1alpha1/tenant/types.go
  • packages/apps/kubernetes/templates/ingress.yaml
  • packages/apps/kubernetes/tests/ingress_proxied_gate_test.yaml
  • packages/apps/tenant/README.md
  • packages/apps/tenant/templates/computeplane.yaml
  • packages/apps/tenant/values.schema.json
  • packages/apps/tenant/values.yaml
  • packages/extra/computeplane/README.md
  • packages/extra/computeplane/templates/cluster.yaml
  • packages/system/computeplane-rd/cozyrds/computeplane.yaml
  • packages/system/tenant-rd/cozyrds/tenant.yaml
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/system/tenant-rd/cozyrds/tenant.yaml
  • packages/apps/tenant/values.yaml
  • packages/apps/kubernetes/templates/ingress.yaml
  • api/apps/v1alpha1/tenant/types.go
  • packages/apps/tenant/values.schema.json
  • packages/apps/kubernetes/tests/ingress_proxied_gate_test.yaml
  • packages/extra/computeplane/templates/cluster.yaml
  • packages/apps/tenant/templates/computeplane.yaml

Comment thread packages/apps/tenant/README.md

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Round three is closed and I verified the fixes rather than reading the summary — rendering apps/kubernetes against the no-etcd values with the addon enabled and exposeMethod: Proxied now produces nothing, which was the one functional bug left; the secrets framing is corrected to the allowlist mechanism with the bats cross-check removed; and re-running the generators for both computeplane and tenant produces zero drift, so the reworded toggle description is genuinely generated across all four copies. Suites green at 9 / 26 / 90 / 186.

Taking exclude: [] / include: [] over defence-in-depth was the right call — a literal name list would have invited the same misreading a second time, and the empty include list withholds by construction regardless of naming.

Two notes, neither blocking and neither needing a change here.

The etcd prerequisite staying documentation rather than a render-time fail is a reasonable call, and the rewritten README is accurate now that ancestor etcd counts and the awaiting-etcd beacon is named. The stated reason does not hold, though: packages/apps/tenant/templates/namespace.yaml computes the effective etcd as $parentNamespace.etcd overridden by .Values.etcd, so enabling etcd and computeplane in a single edit would satisfy a guard in that same render — the enable-both-in-one-edit flow would not have broken. The decision stands on the beacon plus the docs being proportionate; it just should not be cited later as a precedent for skipping a guard that would have worked.

On the maxReplicas >= 1 guard: I misread the template on first pass and thought the fail sat inside the range, which would have made it order-dependent across node groups. It does not — the range closes before the check. Flagging only so the observation is not mistaken for an unreported concern.

Shipping the toggle ahead of in-tree placement routing is fine given cozyllm targets the kubeconfig Secret directly; the capability is not inert, which was the actual worry. Module self-provisioning belonging with community#39's protection capability rather than a special case here is the right home for it.

@kvaps

Copy link
Copy Markdown
Member Author

let's wait for v1.5 release, we'll take it into v1.6

@kvaps

Copy link
Copy Markdown
Member Author

Rebased on main over the kubernetes/kubernetes-nodes split (#3314). The split is additive — the base app keeps nodeGroups and the Proxied ingress Service, so the module wraps apps/kubernetes unchanged; the only conflict was the package line placement in the iaas bundle (computeplane-application now sits after kubernetes-nodes-application). Generators produce zero drift on the new base; suites green: computeplane 9, tenant 26, kubernetes 189, kubernetes-nodes 8, platform 99, bats 199/200 (the one failure is the docker-dependent seaweedfs migration test, unavailable in my local environment — CI will exercise it). Approval will need one more click after this push; merge stays parked for the v1.6 window per the earlier comment.

…the kubernetes app

Add the operator-owned computeplane tenant module per the revised design
(cozystack/community design-proposals/compute-plane): an extra/computeplane
chart that provisions a single-tenant, Cozystack-managed cluster for
untrusted-code apps by deploying the ordinary apps/kubernetes chart with
operator-fixed values (fluxcd, cert-manager, ingress-nginx Proxied). The
package re-declares apps/kubernetes as its own source-only component, so the
module is self-contained; the cluster release is named computeplane so Kamaji
writes the admin kubeconfig to computeplane-admin-kubeconfig — the contract
consumed by placement: ComputePlane applications. The tenant holds no admin
kubeconfig and can set cluster shape (nodeGroups) only.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
Wire the computeplane module into the tenant chart as a bool toggle, matching
the other tenant modules. The module's own Helm release takes the -module
suffix so the canonical release name computeplane (and with it the
computeplane-admin-kubeconfig Secret) stays reserved for the cluster
HelmRelease the module renders.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
…hosts

The proxy Service was only rendered when addons.ingressNginx.hosts was
non-empty, but external consumers — e.g. a ComputePlane app's per-app
management Ingress — need to route to the tenant cluster's ingress-nginx
without the module knowing every app host in advance. Render the Service
whenever exposeMethod is Proxied; the Ingress still requires hosts.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
…ckage

The computeplane-rd component installs an ApplicationDefinition, whose CRD is
served by cozystack-engine — hack/check-applicationdefinition-crd-ordering.bats
enforces the edge for every package with a system/*-rd component.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
…on is enabled

exposeMethod defaults to Proxied while the addon defaults to off, so gating
the Service on exposeMethod alone emitted a selector-less Service — surfaced
in the tenant dashboard — on every cluster that runs no ingress-nginx at all.
Gate on the addon instead; ComputePlane sets enabled: true explicitly, and
the Service still renders without a hosts list.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
…kage

The computeplane-application package ships in the iaas bundle while the
toggle arrives with tenant-rd in the system bundle, and bundles are
independent booleans. Refuse the render with a clear diagnostic instead of
producing a HelmRelease that retries a nonexistent artifact forever.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
nodeGroups was a bare object: typos surfaced as a stuck release deep in the
wrapped chart instead of an admission error, and groups without the
ingress-nginx role silently left the pinned ingress-nginx controller (and
with it the ComputePlane ingress) Pending forever, since supplied groups are
not merged with the wrapped chart's default md0. Reuse the kubernetes app's
nodeGroups typedefs for the schema and refuse rendering when no group
carries the role.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
The dependsOn comment claimed the kubernetes-application edge orders the
addon artifacts the wrapped chart references; variant dependsOn only gates
install: components — the operative effect is ordering the computeplane-rd
install so the ComputePlane kind is not registered before the kubernetes
machinery exists. Also document why the variant is named kubevirt.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
The aggregated API cannot represent spec.releaseName: the conversion
rebuilds HelmRelease specs without it, so the first tenant edit through the
API dropped the module's explicit computeplane-module release name, let it
default to the object name computeplane, and collided with the Helm storage
record the cluster release owned — with the tenant chart writing the old
name back every interval. Drop releaseName from both HelmReleases and derive
names from object names, like every other tenant module. Kamaji now writes
the admin kubeconfig to computeplane-cluster-admin-kubeconfig; the
computeplane-rd secrets.exclude entry follows in the same change, and a bats
test pins the exclusion to the name the cluster release actually produces so
a future rename cannot silently re-expose the credential. Also add the
check-release-name guard the other extra modules carry, and pin the fluxcd
addon's distribution artifact to the flux-operator version vendored in
system/fluxcd-operator instead of :latest.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
… pass-through

The tenant Kubernetes machinery — including the cluster-autoscaler that
brings up the default scale-from-zero md0 group — is backed by the tenant's
etcd module, so a ComputePlane without it provisions no workers and gives no
diagnostic; state the prerequisite in the README. Also state plainly that
unlisted NodeGroup fields pass through to the wrapped chart unvalidated (the
schema generator cannot close the object), with a test pinning that
behaviour.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
…ith hosts set

hosts with the addon disabled used to render the Ingress; the addon gate
makes 'the addon is off' mean nothing renders. Pin the combination so the
behaviour change reads as a decision.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
Gate the Proxied ingress Service on _namespace.etcd like the addon
HelmRelease it fronts, so an etcd-less tenant with the addon pinned on does
not get a selector-less Service (+ test). Correct the secrets framing: the
tenant-visible surface is an allowlist (include: [] withholds everything),
so drop the never-load-bearing exclude entry, the bats cross-check and the
rename-coupling comments, and state the real mechanism. Fix the etcd
prerequisite docs (an ancestor's etcd satisfies the wrapped chart; the
awaiting-etcd beacon is the diagnostic), reword the tenant toggle to stop
advertising the not-yet-shipped placement routing, require maxReplicas >= 1
on the ingress-role node group, and fix comment drift (Service name after
the release rename, placement-hook label, package-guard lookup semantics).

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
The Proxied ingress Service is gated on _namespace.etcd as well as the
addon toggle and exposeMethod, but the comment above it still described
only the addon+exposeMethod gate — comment drift from the etcd-gate
change. State both gates so the comment matches the condition it sits on.

Assisted-By: Claude
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
The fluxcd addon pin was a holdover from the #3150-era model where app
charts rendered nested HelmReleases into the target cluster and needed an
in-cluster helm-controller. Consumers now keep their releases on the
management cluster and remote-apply via spec.kubeConfig, so the management
Flux resolves charts and applies manifests — nothing inside the ComputePlane
reconciles HelmReleases. This also decouples the module from the FluxCD
addon removal (#3379). certManager and ingressNginx stay pinned: Certificate
and Ingress objects land inside the cluster and need their controllers
there.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/extra Issues or PRs related to tenant-specific modules (packages/extra/) area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review do-not-merge/hold Indicates that a PR should not merge because someone has issued /hold kind/feature Categorizes issue or PR as related to a new feature size/XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants