Skip to content

feat(kubernetes): bootstrap tenant workers with Talos via CABPT#2931

Merged
myasnikovdaniil merged 12 commits into
mainfrom
phase1-talos-migration
Jun 26, 2026
Merged

feat(kubernetes): bootstrap tenant workers with Talos via CABPT#2931
myasnikovdaniil merged 12 commits into
mainfrom
phase1-talos-migration

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Supersedes #2610. Same work, rebased cleanly onto current main so it now sits on top of #2867 (the kube-controller-manager VAP type-checker fix). That fix is what was killing the base install in #2610's E2E before the kubernetes app test ever ran, leaving the Talos worker-bootstrap rewrite verified only by code reading. #2610's 67-commit branch is squashed here into 8 logical commits with original authorship preserved (Andrei Kvapil (@kvaps) and IvanHunters as authors); the tree is byte-identical to #2610's head — no code changes, just the rebase onto main (to pick up #2867) plus history cleanup. The detailed review history lives on #2610.

Phase 1 of the Kubernetes-app split design (see cozystack/community#8): replace the Ubuntu+kubeadm worker bootstrap path with Talos, driven by cluster-api-bootstrap-provider-talos (CABPT) and a clastix/talos-csr-signer sidecar embedded in the Kamaji control-plane pod. Existing tenants keep working — old machines roll out and get replaced by Talos workers without manual intervention.

Highlights:

  • Add CABPT (v0.6.12) as a second BootstrapProvider alongside the existing kubeadm one in capi-providers-bootstrap.
  • Bump the Kamaji control-plane provider with an upstream-bound patch that exposes KamajiControlPlane.spec.network.additionalServicePorts, used to publish trustd (50001/TCP) on the apiserver service.
  • Generate Talos PKI (Ed25519 CA + trustd TLS) via cert-manager and stable random Talos secrets (token, clusterId, clusterSecret, bootstrapToken) using the helm lookup-and-reuse pattern.
  • Materialise a kubeadm-format bootstrap-token-<id> Secret inside the tenant kube-system via a Helm post-install/upgrade Job.
  • Render a TalosConfigTemplate (worker machineconfig, generateType: none) and switch the MachineDeployment.bootstrap reference from KubeadmConfigTemplate to TalosConfigTemplate, gated on Talos secrets and the Kamaji apiserver service being ready.
  • Boot workers from the Talos openstack image via a CDI DataVolume (source.http.urlfactory.talos.dev), expose the system disk as virtio-blk with blockSize.custom: logical=512, physical=4096 so 4Ki-native block storage backends (LINSTOR/DRBD) play nicely with QEMU's O_DIRECT writes and SeaBIOS still boots, drop the separate kubelet disk (Talos lays out EPHEMERAL itself).
  • Pin pod/service CIDRs in the worker machineconfig to the tenant ranges (10.243.0.0/16 / 10.95.0.0/16) to avoid Talos's address-overlap diagnostic against the host pod CIDR.
  • Set cluster.controlPlane.endpoint and cilium.k8sServiceHost to the Kamaji apiserver Service ClusterIP (looked up at render time), so bootstrap survives the chicken-and-egg moment where tenant DNS does not exist yet and the host CoreDNS does not serve the tenant zone.

End-to-end on a dev cluster: a fresh tenant brings up a Talos worker, kubelet CSRs get approved (apiserver-client by Kamaji, kubelet-serving by the talos-csr-signer sidecar), Cilium initialises with hostNetwork against the apiserver ClusterIP, CoreDNS comes up, and the node transitions to Ready.

Release note

feat(kubernetes): bootstrap tenant workers with Talos Linux instead of Ubuntu+kubeadm. Existing clusters roll over to Talos workers automatically; new tenants come up on Talos from the start. Powered by cluster-api-bootstrap-provider-talos and the talos-csr-signer sidecar in the Kamaji control-plane pod. Note: the tenant `kubernetes` HelmRelease now reports Ready as soon as Helm completes (Install/Upgrade DisableWait) and no longer blocks on worker/addon readiness — worker-rollout health is tracked by WorkloadMonitor, not the HelmRelease.

Summary by CodeRabbit

Release Notes

New Features

  • Added Talos worker configuration for talos.version and talos.schematicID
  • Introduced nodeHealthCheck (maxUnhealthy, nodeStartupTimeout) for worker remediation tuning
  • Added image overrides for kubectl and talosCsrSigner
  • Exposed additional tenant control-plane Service ports support

Breaking Changes

  • Removed Kubernetes v1.30; supported versions start at v1.31
  • Renamed nodeGroups.ephemeralStoragenodeGroups.diskSize with consolidated Talos system-disk semantics

Removals

  • Removed Ubuntu container-disk images and related containerd patch mechanisms
  • Air-gapped tenant workers temporarily unsupported during the Talos transition

Documentation

  • Updated chart docs, parameters, and GitOps upgrade guidance for version bumps

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b78a1b87-fa2f-4f12-bb52-761aa4f7d501

📥 Commits

Reviewing files that changed from the base of the PR and between b5cc2ae and bf92f3f.

📒 Files selected for processing (6)
  • packages/apps/kubernetes/README.md
  • packages/apps/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-pki.yaml
  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml
  • packages/core/platform/images/migrations/migrations/45
  • packages/core/platform/images/migrations/migrations/46
✅ Files skipped from review due to trivial changes (1)
  • packages/apps/kubernetes/README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/apps/kubernetes/templates/talos/talos-pki.yaml
  • packages/core/platform/images/migrations/migrations/46
  • packages/core/platform/images/migrations/migrations/45
  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml

📝 Walkthrough

Walkthrough

This PR replaces Ubuntu container-disk/kubeadm worker bootstrap with Talos-based workers, adds the Talos bootstrap provider and Talos hook templates, upgrades the Kamaji provider, extends API and values schemas, updates migrations, and wires a HelmRelease disable-wait annotation into the Go server.

Changes

Talos Worker Bootstrap and Provider Upgrades

Layer / File(s) Summary
HelmRelease disable-wait annotation
pkg/config/config.go, pkg/config/config_test.go, pkg/cmd/server/start.go, pkg/registry/apps/application/rest.go, packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
Adds the annotation parser and release config field, wires it into HelmRelease install/upgrade DisableWait, and updates the Kubernetes ApplicationDefinition annotation and timeout settings.
Kubernetes API types
api/apps/v1alpha1/kubernetes/types.go, api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
Adds Talos and NodeHealthCheck config types and fields, adds kubectl and talosCsrSigner image overrides, updates the node disk comment, and removes v1.30 from the ResourcesPreset enum.
Kamaji provider upgrade
packages/system/capi-providers-cpprovider/*, Makefile
Updates the Kamaji provider image and metadata, adds image/update build targets, and backports additionalServicePorts into the provider API, CRDs, and reconciliation wiring.
Talos CABPT provider
packages/system/capi-providers-bootstrap/*
Adds the Talos bootstrap provider manifests, metadata, service-account patch, and template wiring for kubeadm and Talos BootstrapProvider/config map selection.
Kubernetes chart and values
packages/apps/kubernetes/values.yaml, values.schema.json, templates/*, images/*, README.md
Adds Talos and node-health values/schema fields, changes worker rendering to Talos disk and control-plane wiring, adds the talos-csr-signer and kubectl images, removes ubuntu-container-disk artifacts, and updates chart docs.
Talos hook templates
packages/apps/kubernetes/templates/talos/*
Adds the Talos secrets, PKI, reconcile, and bootstrap-token hook templates that create Talos credentials, cert-manager resources, the node-group reconcile job, and the tenant bootstrap-token job.
Platform migrations
packages/core/platform/images/migrations/migrations/45, .../46, packages/core/platform/values.yaml
Adds the keep-annotation migration for Helm-managed kubeadm resources, the tenant Kubernetes version bump migration, and updates the platform migration target version.
Tests and cleanup
packages/apps/kubernetes/tests/*, hack/e2e-apps/run-kubernetes.sh, .github/workflows/pull-requests.yaml
Updates the chart tests for Talos rendering and document ordering, adds new validation suites, removes the containerd regression test and fixtures, extends e2e waits, and saves binary PR patches.

Sequence Diagram(s)

sequenceDiagram
  participant Helm as Helm install/upgrade
  participant Secrets as talos-secrets.yaml
  participant PKI as talos-pki.yaml
  participant RecJob as talos-reconcile-job
  participant BtJob as bootstrap-token-job
  participant KCP as KamajiControlPlane
  participant TCPT as TalosConfigTemplate
  Helm->>Secrets: create or reuse Talos credentials Secret
  Helm->>PKI: render Talos CA and signer TLS cert
  Helm->>RecJob: run post-install/post-upgrade hook
  RecJob->>RecJob: wait for Service ClusterIP, CA, and kube-dns
  RecJob->>KCP: patch certSANs with DNS names and ClusterIP
  RecJob->>TCPT: apply TalosConfigTemplate per node group
  Helm->>BtJob: run post-install/post-upgrade hook
  BtJob->>BtJob: create bootstrap-token Secret and RBAC
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • cozystack/cozystack#2571: Updates the same HelmRelease generation path in pkg/config/config.go, pkg/cmd/server/start.go, and pkg/registry/apps/application/rest.go.
  • cozystack/cozystack#3014: Touches the same Kubernetes version mapping in packages/apps/kubernetes/files/versions.yaml.

Suggested reviewers

  • androndo
  • kvaps
  • sircthulhu
  • IvanHunters

Poem

🐇 I hopped from kubeadm to Talos bright,
With hooks and pods all set just right.
The rabbit grins at certs that bloom,
And bootstrap tokens leave the room.
A disable-wait wink, a version nudge,
Now worker paths no longer budge.

🚥 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 matches the main change: switching tenant worker bootstrap to Talos via CABPT.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch phase1-talos-migration

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
packages/apps/kubernetes/README.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-0.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-0.markdownlint-cli2.jsonc'
}
}


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.

@dosubot dosubot Bot added area/kubernetes Issues or PRs related to the tenant Kubernetes app kind/feature Categorizes issue or PR as related to a new feature labels Jun 16, 2026
@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 a significant architectural shift in the Kubernetes worker bootstrap process by migrating from Ubuntu with kubeadm to Talos Linux. The changes include the integration of the Cluster API Bootstrap Provider for Talos (CABPT), the addition of a CSR signer sidecar for secure certificate management, and updated Helm workflows to handle the asynchronous nature of Talos configuration. This transition improves node security and lifecycle management while maintaining compatibility with existing tenant clusters.

Highlights

  • Talos Integration: Replaced the Ubuntu+kubeadm worker bootstrap process with Talos Linux using the Cluster API Bootstrap Provider for Talos (CABPT).
  • PKI and Secret Management: Introduced a talos-csr-signer sidecar and automated PKI generation via cert-manager to secure worker node bootstrap.
  • Helm Hook Automation: Added post-install/upgrade Jobs to manage bootstrap tokens and RBAC, ensuring deterministic configuration of tenant clusters.
  • Configuration Tuning: Updated node health checks and system disk sizing to support Talos-specific storage requirements and optimized first-boot performance.
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: **/*.diff (2)
    • packages/system/capi-providers-bootstrap/patches/0001-rename-default-sa-to-cabpt-manager.diff
    • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/patches/0001-backport-additional-service-ports-pr347.diff
  • Ignored by pattern: **/zz_generated.*.go (1)
    • api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/pull-requests.yaml
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.

@github-actions github-actions Bot added the size/XXL This PR changes 1000+ lines, ignoring generated files label Jun 16, 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 migrates the Kubernetes tenant worker bootstrap from Ubuntu containerDisk + kubeadm to Talos OS, introducing TalosConfigTemplate, Talos PKI infrastructure, a talos-csr-signer sidecar, and post-install reconcile Jobs to resolve asynchronous dependencies. It also upgrades the Kamaji control plane provider to v0.19.0 and adds platform migrations to handle the transition. The review feedback highlights several critical issues: Dockerfiles reference a non-existent Go version 1.26; the talos-reconcile-job incorrectly injects base64 certificates instead of indented PEM certificates and lacks a document separator in its multi-nodeGroup loop; Makefiles contain shell expansion bugs when patches are empty; and both the bootstrap-token and reconcile Jobs require restricted security contexts, resource limits, and minimized service account token mounting.

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.

# To bump the upstream version, update COMMIT below to the desired master
# SHA and rerun `make image-talos-csr-signer` in packages/apps/kubernetes;
# the regenerated images/talos-csr-signer.tag must be committed alongside.
FROM golang:1.26 AS builder

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.

critical

The Go version 1.26 does not exist yet (the current latest is 1.23). This will cause the Docker build to fail when attempting to pull the base image. Please use a valid stable Go version, such as 1.23.

FROM golang:1.23 AS builder

# which our chart relies on for the talos-trustd sidecar port. Once
# upstream ships a real migration path, drop both this Dockerfile and the
# patch and consume vanilla upstream images directly.
FROM golang:1.26 AS builder

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.

critical

The Go version 1.26 does not exist yet. Please use a valid stable Go version, such as 1.23.

FROM golang:1.23 AS builder

Comment on lines +318 to +329
TALOS_CA_B64=$(kubectl -n "$NS" get secret "${RELEASE}-talos-ca" -o jsonpath='{.data.tls\.crt}' 2>/dev/null || true)
K8S_CA_B64=$(kubectl -n "$NS" get secret "${RELEASE}-ca" -o jsonpath='{.data.ca\.crt}' 2>/dev/null || true)
COREDNS_IP=$(kubectl -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true)
if [ -n "$SVC_IP" ] && [ -n "$TALOS_CA_B64" ] && [ -n "$K8S_CA_B64" ] && [ -n "$COREDNS_IP" ]; then
break
fi
sleep 5
done
if [ -z "$SVC_IP" ] || [ -z "$TALOS_CA_B64" ] || [ -z "$K8S_CA_B64" ] || [ -z "$COREDNS_IP" ]; then
echo "[$(date -u +%H:%M:%S)] timed out waiting for lookup inputs (svc='$SVC_IP' talos-ca=$([ -n "$TALOS_CA_B64" ] && echo ok) k8s-ca=$([ -n "$K8S_CA_B64" ] && echo ok) coredns='$COREDNS_IP')"
exit 1
fi

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.

critical

The kubectl get secret command returns the raw base64-encoded certificate from the secret. Injecting this base64 string directly into the Talos machineconfig ca.crt field will fail because Talos expects a PEM-encoded certificate. Additionally, a multi-line PEM certificate must be properly indented to preserve the YAML structure. Decode the certificates using base64 -d and format them with the correct indentation (28 spaces) using sed.

                TALOS_CA=$(kubectl -n "$NS" get secret "${RELEASE}-talos-ca" -o jsonpath='{.data.tls\.crt}' 2>/dev/null | base64 -d | sed 's/^/                            /' || true)
                K8S_CA=$(kubectl -n "$NS" get secret "${RELEASE}-ca" -o jsonpath='{.data.ca\.crt}' 2>/dev/null | base64 -d | sed 's/^/                            /' || true)
                COREDNS_IP=$(kubectl -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true)
                if [ -n "$SVC_IP" ] && [ -n "$TALOS_CA" ] && [ -n "$K8S_CA" ] && [ -n "$COREDNS_IP" ]; then
                  break
                fi
                sleep 5
              done
              if [ -z "$SVC_IP" ] || [ -z "$TALOS_CA" ] || [ -z "$K8S_CA" ] || [ -z "$COREDNS_IP" ]; then
                echo "[$(date -u +%H:%M:%S)] timed out waiting for lookup inputs (svc='$SVC_IP' talos-ca=$([ -n "$TALOS_CA" ] && echo ok) k8s-ca=$([ -n "$K8S_CA" ] && echo ok) coredns='$COREDNS_IP')"
                exit 1
              fi

Comment on lines +418 to +419
ca:
crt: ${TALOS_CA_B64}

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.

critical

Inject the decoded and properly indented TALOS_CA PEM certificate using the YAML literal block scalar (|).

                        ca:
                          crt: |
${TALOS_CA}

Comment on lines +477 to +478
ca:
crt: ${K8S_CA_B64}

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.

critical

Inject the decoded and properly indented K8S_CA PEM certificate using the YAML literal block scalar (|).

                        ca:
                          crt: |
${K8S_CA}

Comment on lines +54 to +56
containers:
- name: kubectl
image: "{{ default (.Files.Get "images/kubectl.tag" | trim) .Values.images.kubectl }}"

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.

security-medium medium

The kubectl container is running without a restricted securityContext. To adhere to the repository security guidelines, please define a securityContext that drops all capabilities, runs as a non-root user, and disables privilege escalation.

      containers:
        - name: kubectl
          image: "{{ default (.Files.Get "images/kubectl.tag" | trim) .Values.images.kubectl }}"
          securityContext:
            allowPrivilegeEscalation: false
            runAsNonRoot: true
            runAsUser: 65532
            capabilities:
              drop:
                - ALL
            seccompProfile:
              type: RuntimeDefault
References
  1. Flag missing securityContext, containers running as root, or other RBAC/SecurityContext issues. (link)

Comment on lines +51 to +53
spec:
restartPolicy: OnFailure
serviceAccountName: {{ .Release.Name }}-bootstrap-token-tenant

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.

security-medium medium

Since this Job only communicates with the tenant cluster using the mounted admin kubeconfig and does not need to access the management cluster's API, we should disable automounting the service account token to follow the principle of least privilege.

    spec:
      restartPolicy: OnFailure
      automountServiceAccountToken: false
      serviceAccountName: {{ .Release.Name }}-bootstrap-token-tenant
References
  1. Flag missing securityContext, containers running as root, or other RBAC/SecurityContext issues. (link)

Comment on lines +281 to +283
containers:
- name: kubectl
image: "{{ default ($.Files.Get "images/kubectl.tag" | trim) $.Values.images.kubectl }}"

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.

security-medium medium

The kubectl container in the talos-reconcile Job is running without a restricted securityContext. Please define a securityContext to drop all capabilities, run as a non-root user, and disable privilege escalation.

      containers:
        - name: kubectl
          image: "{{ default ($.Files.Get "images/kubectl.tag" | trim) $.Values.images.kubectl }}"
          securityContext:
            allowPrivilegeEscalation: false
            runAsNonRoot: true
            runAsUser: 65532
            capabilities:
              drop:
                - ALL
            seccompProfile:
              type: RuntimeDefault
References
  1. Flag missing securityContext, containers running as root, or other RBAC/SecurityContext issues. (link)

Comment on lines +31 to +33
for p in patches/*.diff; do \
(cd .update-tmp && patch bootstrap-components.yaml < ../$$p) || exit 1; \
done

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

If there are no .diff files in the patches/ directory, the shell expansion patches/*.diff will remain literal, causing the loop to fail. Add a check to ensure the file exists before attempting to apply the patch.

	for p in patches/*.diff; do \
		[ -e "$$p" ] || continue; \
		(cd .update-tmp && patch bootstrap-components.yaml < ../$$p) || exit 1; \
		done

Comment on lines +46 to +48
for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
git apply --recount "$$p"; \
done

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

If there are no .diff files in the patches/ directory, the shell expansion will remain literal and cause the git apply command to fail. Add a check to ensure the file exists before applying.

		for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
			[ -e "$$p" ] || continue; \
			git apply --recount "$$p"; \
		done

@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: 7

🤖 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/kubernetes/images/kubectl.tag`:
- Line 1: Update the kubectl version pinned in the kubectl.tag file from 1.33.4
to a version that supports the full Kubernetes tenant range (v1.31–v1.35). The
current version 1.33.4 violates the Kubernetes Version Skew Policy and breaks
the bootstrap-token job for tenants running v1.31 or v1.35. Update the version
to 1.34 or another version that accommodates the full v1.31–v1.35 window while
maintaining compatibility with the version skew requirements (kubectl must be
within ±1 minor version of kube-apiserver).

In `@packages/apps/kubernetes/templates/cluster.yaml`:
- Around line 608-614: The maxUnhealthy field currently uses the `| int` filter
to coerce non-percentage values to integers, which can silently accept invalid
input and change remediation behavior unexpectedly. Add validation logic that
checks whether non-percentage values (those not ending with "%") are valid
integers before attempting coercion; if the value is not a valid integer and not
a percentage string, the template should fail with a clear error message rather
than silently coercing the invalid input through the `| int` filter.

In `@packages/core/platform/images/migrations/migrations/44`:
- Around line 45-80: The HELM_RELEASE_LABEL constant is set to filter by an
annotation (meta.helm.sh/release-name) instead of a label, causing the kubectl
selector in the list_helm_managed function to return no objects. Update the
HELM_RELEASE_LABEL constant definition to use the standard Helm-managed label
app.kubernetes.io/managed-by=Helm instead, which Helm automatically injects into
all managed resources. This will ensure the kubectl get command in
list_helm_managed properly identifies and returns Helm-managed objects that need
to be annotated with the resource policy.

In
`@packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml`:
- Around line 652-654: The targetPort in the https port definition for the
metrics Service is set to `https`, but the corresponding Deployment container
only exposes a port named `metrics` at port 8443. Change the targetPort value
from `https` to `metrics` in the Service definition so that traffic is correctly
routed to the container port that actually exists.

In `@packages/system/capi-providers-cpprovider/Makefile`:
- Around line 45-48: In the update target, the git commit command at line 45 is
failing when git user.name and user.email are not configured globally, but this
commit is unnecessary for patch application. Remove the `git commit -qm init &&`
portion from the command chain in the update target while keeping the `git init
-q` and `git add .` commands, since only these are required for the subsequent
git apply operations to work correctly.

In `@packages/system/kubernetes-rd/cozyrds/kubernetes.yaml`:
- Line 35: The nodeHealthCheck fields maxUnhealthy and nodeStartupTimeout lack
validation patterns, allowing invalid values to pass API validation and cause
issues during chart rendering. Rather than editing the generated YAML directly,
locate the cozyvalues-gen source input file that defines this schema and add
proper validation patterns there: add a pattern constraint for maxUnhealthy to
enforce integer-or-percent format (e.g., bare integers or percentage strings
like "50%"), and add a duration pattern constraint for nodeStartupTimeout (e.g.,
durations like "10m"). Then regenerate the downstream kubernetes.yaml artifact
from the cozyvalues-gen source to ensure schema consistency across all generated
outputs.

In `@pkg/config/config.go`:
- Around line 74-89: The ParseHelmInstallDisableWaitAnnotation function
documents case-insensitive boolean parsing but only handles specific hardcoded
variations like "true", "True", "TRUE", so mixed-case inputs like "tRue"
incorrectly error. Fix this by converting the input string to lowercase once at
the beginning (using strings.ToLower or similar) and then checking against the
lowercase values "true" and "false", which makes it genuinely case-insensitive.
Also update the TestParseHelmInstallDisableWaitAnnotation test to include
mixed-case test cases and verify they pass with the corrected implementation.
🪄 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: e4c773d1-f593-458c-8bbb-94537369f537

📥 Commits

Reviewing files that changed from the base of the PR and between 39f3ac7 and e46b258.

⛔ Files ignored due to path filters (2)
  • packages/system/capi-providers-bootstrap/files/components-talos.gz is excluded by !**/*.gz
  • packages/system/capi-providers-cpprovider/files/components.gz is excluded by !**/*.gz
📒 Files selected for processing (61)
  • .github/workflows/pull-requests.yaml
  • Makefile
  • api/apps/v1alpha1/kubernetes/types.go
  • api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
  • hack/e2e-apps/run-kubernetes.sh
  • packages/apps/kubernetes/Makefile
  • packages/apps/kubernetes/README.md
  • packages/apps/kubernetes/files/versions.yaml
  • packages/apps/kubernetes/images/kubectl.tag
  • packages/apps/kubernetes/images/talos-csr-signer.tag
  • packages/apps/kubernetes/images/talos-csr-signer/Dockerfile
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk/Dockerfile
  • packages/apps/kubernetes/templates/_helpers.tpl
  • packages/apps/kubernetes/templates/cluster.yaml
  • packages/apps/kubernetes/templates/copy-patch-containerd.yaml
  • packages/apps/kubernetes/templates/helmreleases/cilium.yaml
  • packages/apps/kubernetes/templates/helmreleases/ouroboros.yaml
  • packages/apps/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-pki.yaml
  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-secrets.yaml
  • packages/apps/kubernetes/tests/cluster_test.yaml
  • packages/apps/kubernetes/tests/containerd-config-path-test.sh
  • packages/apps/kubernetes/tests/fixtures/containerd-1x.toml
  • packages/apps/kubernetes/tests/fixtures/containerd-2x.toml
  • packages/apps/kubernetes/tests/gpu_node_labels_test.yaml
  • packages/apps/kubernetes/tests/kubelet-reservation_test.yaml
  • packages/apps/kubernetes/tests/kubelet_reservation_test.yaml
  • packages/apps/kubernetes/tests/mhc_rendering_test.yaml
  • packages/apps/kubernetes/tests/talos_k8s_support_matrix_test.yaml
  • packages/apps/kubernetes/tests/talos_templates_test.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/apps/kubernetes/values.yaml
  • packages/core/platform/images/migrations/migrations/44
  • packages/core/platform/images/migrations/migrations/45
  • packages/core/platform/values.yaml
  • packages/system/capi-providers-bootstrap/Makefile
  • packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml
  • packages/system/capi-providers-bootstrap/files/metadata-talos.yaml
  • packages/system/capi-providers-bootstrap/patches/0001-rename-default-sa-to-cabpt-manager.diff
  • packages/system/capi-providers-bootstrap/templates/configmaps.yaml
  • packages/system/capi-providers-bootstrap/templates/providers.yaml
  • packages/system/capi-providers-cpprovider/Makefile
  • packages/system/capi-providers-cpprovider/files/control-plane-components.yaml
  • packages/system/capi-providers-cpprovider/files/metadata.yaml
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji.tag
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/patches/0001-backport-additional-service-ports-pr347.diff
  • packages/system/capi-providers-cpprovider/templates/configmaps.yaml
  • packages/system/capi-providers-cpprovider/templates/providers.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • pkg/cmd/server/start.go
  • pkg/config/config.go
  • pkg/config/config_test.go
  • pkg/registry/apps/application/rest.go
💤 Files with no reviewable changes (13)
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk/Dockerfile
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag
  • packages/apps/kubernetes/templates/copy-patch-containerd.yaml
  • packages/apps/kubernetes/tests/fixtures/containerd-2x.toml
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag
  • packages/apps/kubernetes/tests/containerd-config-path-test.sh
  • packages/apps/kubernetes/files/versions.yaml
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag
  • packages/apps/kubernetes/tests/kubelet-reservation_test.yaml
  • packages/apps/kubernetes/tests/fixtures/containerd-1x.toml
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag

@@ -0,0 +1 @@
docker.io/alpine/k8s:1.33.4@sha256:b0523f0a244ddc4c8e055aa335c040d3d78b3ead5528f4544395f7f9f69c7b68

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

🧩 Analysis chain

🌐 Web query:

According to the official Kubernetes Version Skew Policy, which kubectl minor versions are supported against kube-apiserver 1.31 and 1.35?

💡 Result:

According to the official Kubernetes Version Skew Policy, kubectl is supported if its minor version is within one minor version (older or newer) of the kube-apiserver [1][2]. For the specified kube-apiserver versions, the supported kubectl minor versions are as follows: - Against kube-apiserver 1.31: kubectl minor versions 1.30, 1.31, and 1.32 are supported [1][2]. - Against kube-apiserver 1.35: kubectl minor versions 1.34, 1.35, and 1.36 are supported [1][2]. In environments where multiple kube-apiserver instances exist (such as in a highly available cluster), if there is a version skew between the API server instances themselves, the supported range for kubectl is narrowed to the intersection of the supported ranges for all active API server versions [1].

Citations:


Update kubectl minor version to support the full Kubernetes tenant range (v1.31–v1.35).

Line 1 pins kubectl to version 1.33.4. However, this violates Kubernetes Version Skew Policy across the chart's supported tenant versions:

  • kubectl 1.33 requires kube-apiserver 1.32–1.34 (within ±1 minor)
  • Against kube-apiserver 1.31: only kubectl 1.30–1.32 are supported
  • Against kube-apiserver 1.35: only kubectl 1.34–1.36 are supported

This breaks the bootstrap-token job for tenants running Kubernetes v1.31 or v1.35. Update the pinned version to one that spans the supported range (e.g., 1.34 for v1.33–v1.36 compatibility, or prefer a version that accommodates the full v1.31–v1.35 window if tool constraints allow).

🤖 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/apps/kubernetes/images/kubectl.tag` at line 1, Update the kubectl
version pinned in the kubectl.tag file from 1.33.4 to a version that supports
the full Kubernetes tenant range (v1.31–v1.35). The current version 1.33.4
violates the Kubernetes Version Skew Policy and breaks the bootstrap-token job
for tenants running v1.31 or v1.35. Update the version to 1.34 or another
version that accommodates the full v1.31–v1.35 window while maintaining
compatibility with the version skew requirements (kubectl must be within ±1
minor version of kube-apiserver).

Comment thread packages/apps/kubernetes/templates/cluster.yaml
Comment thread packages/core/platform/images/migrations/migrations/44 Outdated
Comment on lines +652 to +654
- name: https
port: 8443
targetPort: https

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify that the metrics Service targetPort resolves to a named container port.

set -euo pipefail

file="packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml"

printf 'Metrics Service port block:\n'
sed -n '650,655p' "$file"

printf '\nDeployment container port block:\n'
sed -n '709,718p' "$file"

# Expected after the fix:
# - Service targetPort is "metrics", or
# - the Deployment exposes a container port named "https".

Repository: cozystack/cozystack

Length of output: 461


Update the metrics Service targetPort to match the exposed container port name.

The cabpt-controller-manager-metrics-service specifies targetPort: https, but the Deployment container only exposes a port named metrics (at port 8443). The Service will not route traffic correctly.

Fix
   ports:
   - name: https
     port: 8443
-    targetPort: https
+    targetPort: metrics
📝 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
- name: https
port: 8443
targetPort: https
- name: https
port: 8443
targetPort: metrics
🤖 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/capi-providers-bootstrap/files/bootstrap-components-talos.yaml`
around lines 652 - 654, The targetPort in the https port definition for the
metrics Service is set to `https`, but the corresponding Deployment container
only exposes a port named `metrics` at port 8443. Change the targetPort value
from `https` to `metrics` in the Service definition so that traffic is correctly
routed to the container port that actually exists.

Comment on lines +45 to +48
cd .update-tmp && git init -q && git add . && git commit -qm init && \
for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
git apply --recount "$$p"; \
done

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

Remove the local git commit dependency from update target.

At Line 45, git commit -qm init can fail when user.name/user.email are not configured, and patch application here does not require a commit.

🛠️ Proposed fix
-	cd .update-tmp && git init -q && git add . && git commit -qm init && \
+	cd .update-tmp && git init -q && \
 		for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
 			git apply --recount "$$p"; \
 		done
📝 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
cd .update-tmp && git init -q && git add . && git commit -qm init && \
for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
git apply --recount "$$p"; \
done
cd .update-tmp && git init -q && \
for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
git apply --recount "$$p"; \
done
🤖 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/capi-providers-cpprovider/Makefile` around lines 45 - 48, In
the update target, the git commit command at line 45 is failing when git
user.name and user.email are not configured globally, but this commit is
unnecessary for patch application. Remove the `git commit -qm init &&` portion
from the command chain in the update target while keeping the `git init -q` and
`git add .` commands, since only these are required for the subsequent git apply
operations to work correctly.

plural: kuberneteses
openAPISchema: |-
{"title":"Chart Values","type":"object","properties":{"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated","x-kubernetes-validations":[{"rule":"self == oldSelf","message":"storageClass is immutable"}],"x-cozystack-options":{"source":"storageclass"}},"nodeGroups":{"description":"Worker nodes configuration map.","type":"object","default":{"md0":{"diskSize":"20Gi","gpus":[],"instanceType":"u1.medium","kubelet":{},"maxReplicas":10,"minReplicas":0,"resources":{},"roles":["ingress-nginx"],"storageClass":""}},"additionalProperties":{"type":"object","required":["diskSize","instanceType","maxReplicas","minReplicas","resources"],"properties":{"diskSize":{"description":"Persistent disk size for kubelet and containerd data.","default":"20Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"description":"Name of GPU, such as \"nvidia.com/AD102GL_L40S\".","type":"string","x-cozystack-options":{"source":"gpu"}}}}},"instanceType":{"description":"Virtual machine instance type.","type":"string","default":"u1.medium","x-cozystack-options":{"source":"instancetype"}},"kubelet":{"description":"Kubelet resource reservations for this node group.","type":"object","properties":{"evictionHardMemory":{"description":"Hard eviction threshold for memory (absolute like 200Mi or percentage like 7%).","type":"string","default":"7%"},"evictionSoftMemory":{"description":"Soft eviction threshold for memory (absolute like 1Gi or percentage like 10%).","type":"string","default":"10%"},"kubeReservedCpu":{"description":"CPU reserved for kubelet and container runtime. Auto-computed from instanceType if empty.","type":"string"},"kubeReservedMemory":{"description":"Memory reserved for kubelet and container runtime. Auto-computed from instanceType if empty.","type":"string"},"systemReservedCpu":{"description":"CPU reserved for host OS. Auto-computed from instanceType if empty.","type":"string"},"systemReservedMemory":{"description":"Memory reserved for host OS. Auto-computed from instanceType if empty.","type":"string"}}},"maxReplicas":{"description":"Maximum number of replicas.","type":"integer","default":10},"minReplicas":{"description":"Minimum number of replicas.","type":"integer","default":0},"resources":{"description":"CPU and memory resources for each worker node.","type":"object","properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"roles":{"description":"List of node roles.","type":"array","items":{"type":"string"}},"storageClass":{"description":"StorageClass for worker node persistent disks. When empty, uses the management cluster default StorageClass (the one annotated storageclass.kubernetes.io/is-default-class: true). NOTE: deliberately not marked immutable — the field is optional and undefaulted, so a strict `self == oldSelf` rule would block any future attempt to set it on an existing node group.","type":"string","x-cozystack-options":{"source":"storageclass"}}}}},"version":{"description":"Kubernetes major.minor version to deploy","type":"string","default":"v1.35","enum":["v1.35","v1.34","v1.33","v1.32","v1.31","v1.30"]},"host":{"description":"External hostname for Kubernetes cluster. Defaults to `<cluster-name>.<tenant-host>` if empty.","type":"string","default":""},"addons":{"description":"Cluster addons configuration.","type":"object","default":{},"required":["certManager","cilium","coredns","fluxcd","gatewayAPI","gpuOperator","hami","ingressNginx","monitoringAgents","ouroboros","velero","verticalPodAutoscaler"],"properties":{"certManager":{"description":"Cert-manager addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable cert-manager.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"cilium":{"description":"Cilium CNI plugin.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"coredns":{"description":"CoreDNS addon.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"fluxcd":{"description":"FluxCD GitOps operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable FluxCD.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"gatewayAPI":{"description":"Gateway API addon.","type":"object","default":{},"required":["enabled"],"properties":{"enabled":{"description":"Enable Gateway API.","type":"boolean","default":false}}},"gpuOperator":{"description":"NVIDIA GPU Operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable GPU Operator.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"hami":{"description":"HAMi GPU virtualization middleware.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable HAMi (requires GPU Operator).","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ingressNginx":{"description":"Ingress-NGINX controller.","type":"object","default":{},"required":["enabled","exposeMethod","valuesOverride"],"properties":{"enabled":{"description":"Enable the controller (requires nodes labeled `ingress-nginx`).","type":"boolean","default":false},"exposeMethod":{"description":"Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`.","type":"string","default":"Proxied","enum":["Proxied","LoadBalancer"]},"hosts":{"description":"Domains routed to this tenant cluster when `exposeMethod` is `Proxied`.","type":"array","default":[],"items":{"type":"string"}},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"monitoringAgents":{"description":"Monitoring agents.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable monitoring agents.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ouroboros":{"description":"Hairpin-NAT fix for ingress-nginx with PROXY-protocol.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable ouroboros. Requires addons.ingressNginx.enabled (chart-render fail otherwise). Only useful when PROXY-protocol is wired on the tenant ingress-nginx via valuesOverride.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides. Operator-key wins over cozystack defaults.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"velero":{"description":"Velero backup/restore addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable Velero.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"verticalPodAutoscaler":{"description":"Vertical Pod Autoscaler.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}}}},"controlPlane":{"description":"Kubernetes control-plane configuration.","type":"object","default":{},"required":["apiServer","controllerManager","konnectivity","replicas","scheduler"],"properties":{"apiServer":{"description":"API Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for API Server.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"c1.medium","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}},"controllerManager":{"description":"Controller Manager configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Controller Manager.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"t1.micro","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}},"konnectivity":{"description":"Konnectivity configuration.","type":"object","default":{},"required":["server"],"properties":{"server":{"description":"Konnectivity Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Konnectivity.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"t1.micro","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"replicas":{"description":"Number of control-plane replicas.","type":"integer","default":2},"scheduler":{"description":"Scheduler configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Scheduler.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"t1.micro","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"images":{"description":"Optional image overrides for air-gapped or rate-limited registries.","type":"object","default":{},"properties":{"waitForKubeconfig":{"description":"Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag.","type":"string","default":""}}}}}
{"title":"Chart Values","type":"object","properties":{"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated","x-kubernetes-validations":[{"rule":"self == oldSelf","message":"storageClass is immutable"}],"x-cozystack-options":{"source":"storageclass"}},"nodeGroups":{"description":"Worker nodes configuration map.","type":"object","default":{"md0":{"diskSize":"20Gi","gpus":[],"instanceType":"u1.medium","kubelet":{},"maxReplicas":10,"minReplicas":0,"resources":{},"roles":["ingress-nginx"],"storageClass":""}},"additionalProperties":{"type":"object","required":["diskSize","instanceType","maxReplicas","minReplicas","resources"],"properties":{"diskSize":{"description":"System disk size for the worker VM. Carries the Talos OS image (factory.talos.dev raw artifact streamed in by CDI), kubelet state, containerd image cache, and any local-path PVCs. Pre-Talos installs used a separate disk-kubelet PVC for kubelet/containerd state; on Talos this is consolidated onto the single system disk imaged from the factory artifact.","default":"20Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"description":"Name of GPU, such as \"nvidia.com/AD102GL_L40S\".","type":"string","x-cozystack-options":{"source":"gpu"}}}}},"instanceType":{"description":"Virtual machine instance type.","type":"string","default":"u1.medium","x-cozystack-options":{"source":"instancetype"}},"kubelet":{"description":"Kubelet resource reservations for this node group.","type":"object","properties":{"evictionHardMemory":{"description":"Hard eviction threshold for memory (absolute like 200Mi or percentage like 7%).","type":"string","default":"7%"},"evictionSoftMemory":{"description":"Soft eviction threshold for memory (absolute like 1Gi or percentage like 10%).","type":"string","default":"10%"},"kubeReservedCpu":{"description":"CPU reserved for kubelet and container runtime. Auto-computed from instanceType if empty.","type":"string"},"kubeReservedMemory":{"description":"Memory reserved for kubelet and container runtime. Auto-computed from instanceType if empty.","type":"string"},"systemReservedCpu":{"description":"CPU reserved for host OS. Auto-computed from instanceType if empty.","type":"string"},"systemReservedMemory":{"description":"Memory reserved for host OS. Auto-computed from instanceType if empty.","type":"string"}}},"maxReplicas":{"description":"Maximum number of replicas.","type":"integer","default":10},"minReplicas":{"description":"Minimum number of replicas.","type":"integer","default":0},"resources":{"description":"CPU and memory resources for each worker node.","type":"object","properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"roles":{"description":"List of node roles.","type":"array","items":{"type":"string"}},"storageClass":{"description":"StorageClass for worker node persistent disks. When empty, uses the management cluster default StorageClass (the one annotated storageclass.kubernetes.io/is-default-class: true). NOTE: deliberately not marked immutable — the field is optional and undefaulted, so a strict `self == oldSelf` rule would block any future attempt to set it on an existing node group.","type":"string","x-cozystack-options":{"source":"storageclass"}}}}},"version":{"description":"Kubernetes major.minor version to deploy","type":"string","default":"v1.35","enum":["v1.35","v1.34","v1.33","v1.32","v1.31"]},"host":{"description":"External hostname for Kubernetes cluster. Defaults to `<cluster-name>.<tenant-host>` if empty.","type":"string","default":""},"addons":{"description":"Cluster addons configuration.","type":"object","default":{},"required":["certManager","cilium","coredns","fluxcd","gatewayAPI","gpuOperator","hami","ingressNginx","monitoringAgents","ouroboros","velero","verticalPodAutoscaler"],"properties":{"certManager":{"description":"Cert-manager addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable cert-manager.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"cilium":{"description":"Cilium CNI plugin.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"coredns":{"description":"CoreDNS addon.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"fluxcd":{"description":"FluxCD GitOps operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable FluxCD.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"gatewayAPI":{"description":"Gateway API addon.","type":"object","default":{},"required":["enabled"],"properties":{"enabled":{"description":"Enable Gateway API.","type":"boolean","default":false}}},"gpuOperator":{"description":"NVIDIA GPU Operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable GPU Operator.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"hami":{"description":"HAMi GPU virtualization middleware.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable HAMi (requires GPU Operator).","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ingressNginx":{"description":"Ingress-NGINX controller.","type":"object","default":{},"required":["enabled","exposeMethod","valuesOverride"],"properties":{"enabled":{"description":"Enable the controller (requires nodes labeled `ingress-nginx`).","type":"boolean","default":false},"exposeMethod":{"description":"Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`.","type":"string","default":"Proxied","enum":["Proxied","LoadBalancer"]},"hosts":{"description":"Domains routed to this tenant cluster when `exposeMethod` is `Proxied`.","type":"array","default":[],"items":{"type":"string"}},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"monitoringAgents":{"description":"Monitoring agents.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable monitoring agents.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ouroboros":{"description":"Hairpin-NAT fix for ingress-nginx with PROXY-protocol.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable ouroboros. Requires addons.ingressNginx.enabled (chart-render fail otherwise). Only useful when PROXY-protocol is wired on the tenant ingress-nginx via valuesOverride.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides. Operator-key wins over cozystack defaults.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"velero":{"description":"Velero backup/restore addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable Velero.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"verticalPodAutoscaler":{"description":"Vertical Pod Autoscaler.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}}}},"controlPlane":{"description":"Kubernetes control-plane configuration.","type":"object","default":{},"required":["apiServer","controllerManager","konnectivity","replicas","scheduler"],"properties":{"apiServer":{"description":"API Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for API Server.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"c1.medium","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}},"controllerManager":{"description":"Controller Manager configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Controller Manager.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"t1.micro","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}},"konnectivity":{"description":"Konnectivity configuration.","type":"object","default":{},"required":["server"],"properties":{"server":{"description":"Konnectivity Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Konnectivity.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"t1.micro","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"replicas":{"description":"Number of control-plane replicas.","type":"integer","default":2},"scheduler":{"description":"Scheduler configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Scheduler.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"t1.micro","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"images":{"description":"Optional image overrides for air-gapped or rate-limited registries.","type":"object","default":{},"properties":{"kubectl":{"description":"Image used by the bootstrap-token tenant Job (kubectl). Empty falls back to images/kubectl.tag.","type":"string","default":""},"talosCsrSigner":{"description":"Image used by the talos-csr-signer sidecar in the Kamaji control plane. Empty falls back to images/talos-csr-signer.tag.","type":"string","default":""},"waitForKubeconfig":{"description":"Image used by the wait-for-kubeconfig init container. Empty falls back to images/busybox.tag.","type":"string","default":""}}},"talos":{"description":"Talos worker image configuration.","type":"object","default":{},"required":["schematicID","version"],"properties":{"schematicID":{"description":"Talos image-factory schematic ID. Defaults to the cozystack-tested vanilla schematic. Operators using custom schematics (system extensions, kernel args) override here.","type":"string","default":"ce4c980550dd2ab1b17bbf2b08801c7eb59418eafe8f279833297925d67c7515"},"version":{"description":"Talos release used for worker OS image and installer. Must satisfy the chart's Talos<->Kubernetes support matrix against the chosen `version`.","type":"string","default":"v1.13.0"}}},"nodeHealthCheck":{"description":"MachineHealthCheck tuning for worker node groups.","type":"object","default":{},"required":["maxUnhealthy","nodeStartupTimeout"],"properties":{"maxUnhealthy":{"description":"Maximum number of unhealthy nodes tolerated per node group before remediation is paused. The MHC admission webhook accepts either a bare integer (\"0\", \"1\", ...) or a percentage (\"0%\", \"50%\"); bare numeric strings are rejected, so the safer default is to express the value as a percentage. Default \"50%\" leaves headroom for transient unhealthy nodes during the kubeadm-to-Talos rollover and slow first boots from factory.talos.dev. Drop to \"0%\" once the fleet is stable on Talos workers.","type":"string","default":"50%"},"nodeStartupTimeout":{"description":"Maximum time a Machine is allowed to spend reaching the Ready condition before it is remediated. Raise for slow first boots (Talos image fetch from factory.talos.dev or a busy storage class on the kubevirt-csi PVC populator).","type":"string","default":"10m"}}}}}

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

Add explicit validation for new nodeHealthCheck string fields to prevent silent misconfiguration.

nodeHealthCheck.maxUnhealthy and nodeHealthCheck.nodeStartupTimeout are currently unconstrained strings. Invalid values can pass API validation and then be coerced unexpectedly during chart rendering, which may alter MachineHealthCheck behavior in production.

Please enforce formats at schema source (e.g., maxUnhealthy as integer-or-percent pattern; nodeStartupTimeout as duration pattern), then regenerate downstream artifacts so CRD/type contracts stay aligned.

Based on learnings, this repository expects enum/validation consistency to be driven from cozyvalues-gen inputs rather than hand-editing generated outputs.

🤖 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/kubernetes-rd/cozyrds/kubernetes.yaml` at line 35, The
nodeHealthCheck fields maxUnhealthy and nodeStartupTimeout lack validation
patterns, allowing invalid values to pass API validation and cause issues during
chart rendering. Rather than editing the generated YAML directly, locate the
cozyvalues-gen source input file that defines this schema and add proper
validation patterns there: add a pattern constraint for maxUnhealthy to enforce
integer-or-percent format (e.g., bare integers or percentage strings like
"50%"), and add a duration pattern constraint for nodeStartupTimeout (e.g.,
durations like "10m"). Then regenerate the downstream kubernetes.yaml artifact
from the cozyvalues-gen source to ensure schema consistency across all generated
outputs.

Source: Learnings

Comment thread pkg/config/config.go
@myasnikovdaniil myasnikovdaniil force-pushed the phase1-talos-migration branch from e46b258 to 1ecbf37 Compare June 22, 2026 05:14

@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

♻️ Duplicate comments (1)
packages/system/capi-providers-cpprovider/Makefile (1)

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

Drop the unnecessary git commit step from update

git apply here does not require a commit, and git commit -qm init can fail when user.name/user.email are unset in CI or clean dev environments.

Suggested fix
-	cd .update-tmp && git init -q && git add . && git commit -qm init && \
+	cd .update-tmp && git init -q && git add . && \
 		for p in ../images/cluster-api-control-plane-provider-kamaji/patches/*.diff; do \
 			[ -e "$$p" ] || continue; \
 			git apply --recount "$$p"; \
 		done
#!/bin/bash
set -euo pipefail

rg -n 'git commit -qm init' packages/system/capi-providers-cpprovider/Makefile -n -C2
🤖 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/capi-providers-cpprovider/Makefile` at line 45, The update
target in the Makefile contains an unnecessary git commit step that can fail in
CI or clean environments when user.name and user.email are unset. Remove the
`git commit -qm init` portion from the command chain while keeping the `git init
-q` and `git add .` steps, as these are still needed for the subsequent git
apply operation.
🤖 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/kubernetes/templates/talos/talos-reconcile-job.yaml`:
- Around line 75-78: The RBAC rule for kamajicontrolplanes in the
talos-reconcile-job ServiceAccount is missing the required `update` verb on the
`finalizers` subresource. When the Job attempts to create TalosConfigTemplate
with blockOwnerDeletion set to true, Kubernetes'
OwnerReferencesPermissionEnforcement admission controller enforces that the
ServiceAccount must have update permissions on the owner resource's finalizers.
Add a new RBAC rule with apiGroup controlplane.cluster.x-k8s.io, resources
kamajicontrolplanes/finalizers, resourceNames matching the release name, and
verbs containing only update. This same pattern needs to be applied to the other
kamajicontrolplanes-related rules mentioned in the also applies to section.

---

Duplicate comments:
In `@packages/system/capi-providers-cpprovider/Makefile`:
- Line 45: The update target in the Makefile contains an unnecessary git commit
step that can fail in CI or clean environments when user.name and user.email are
unset. Remove the `git commit -qm init` portion from the command chain while
keeping the `git init -q` and `git add .` steps, as these are still needed for
the subsequent git apply operation.
🪄 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: 1844b908-fd83-4a86-bbdd-8d72fee9721e

📥 Commits

Reviewing files that changed from the base of the PR and between e46b258 and 1ecbf37.

⛔ Files ignored due to path filters (2)
  • packages/system/capi-providers-bootstrap/files/components-talos.gz is excluded by !**/*.gz
  • packages/system/capi-providers-cpprovider/files/components.gz is excluded by !**/*.gz
📒 Files selected for processing (61)
  • .github/workflows/pull-requests.yaml
  • Makefile
  • api/apps/v1alpha1/kubernetes/types.go
  • api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
  • hack/e2e-apps/run-kubernetes.sh
  • packages/apps/kubernetes/Makefile
  • packages/apps/kubernetes/README.md
  • packages/apps/kubernetes/files/versions.yaml
  • packages/apps/kubernetes/images/kubectl.tag
  • packages/apps/kubernetes/images/talos-csr-signer.tag
  • packages/apps/kubernetes/images/talos-csr-signer/Dockerfile
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk/Dockerfile
  • packages/apps/kubernetes/templates/_helpers.tpl
  • packages/apps/kubernetes/templates/cluster.yaml
  • packages/apps/kubernetes/templates/copy-patch-containerd.yaml
  • packages/apps/kubernetes/templates/helmreleases/cilium.yaml
  • packages/apps/kubernetes/templates/helmreleases/ouroboros.yaml
  • packages/apps/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-pki.yaml
  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-secrets.yaml
  • packages/apps/kubernetes/tests/cluster_test.yaml
  • packages/apps/kubernetes/tests/containerd-config-path-test.sh
  • packages/apps/kubernetes/tests/fixtures/containerd-1x.toml
  • packages/apps/kubernetes/tests/fixtures/containerd-2x.toml
  • packages/apps/kubernetes/tests/gpu_node_labels_test.yaml
  • packages/apps/kubernetes/tests/kubelet-reservation_test.yaml
  • packages/apps/kubernetes/tests/kubelet_reservation_test.yaml
  • packages/apps/kubernetes/tests/mhc_rendering_test.yaml
  • packages/apps/kubernetes/tests/talos_k8s_support_matrix_test.yaml
  • packages/apps/kubernetes/tests/talos_templates_test.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/apps/kubernetes/values.yaml
  • packages/core/platform/images/migrations/migrations/44
  • packages/core/platform/images/migrations/migrations/45
  • packages/core/platform/values.yaml
  • packages/system/capi-providers-bootstrap/Makefile
  • packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml
  • packages/system/capi-providers-bootstrap/files/metadata-talos.yaml
  • packages/system/capi-providers-bootstrap/patches/0001-rename-default-sa-to-cabpt-manager.diff
  • packages/system/capi-providers-bootstrap/templates/configmaps.yaml
  • packages/system/capi-providers-bootstrap/templates/providers.yaml
  • packages/system/capi-providers-cpprovider/Makefile
  • packages/system/capi-providers-cpprovider/files/control-plane-components.yaml
  • packages/system/capi-providers-cpprovider/files/metadata.yaml
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji.tag
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/patches/0001-backport-additional-service-ports-pr347.diff
  • packages/system/capi-providers-cpprovider/templates/configmaps.yaml
  • packages/system/capi-providers-cpprovider/templates/providers.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • pkg/cmd/server/start.go
  • pkg/config/config.go
  • pkg/config/config_test.go
  • pkg/registry/apps/application/rest.go
💤 Files with no reviewable changes (13)
  • packages/apps/kubernetes/templates/copy-patch-containerd.yaml
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk/Dockerfile
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag
  • packages/apps/kubernetes/tests/fixtures/containerd-1x.toml
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag
  • packages/apps/kubernetes/files/versions.yaml
  • packages/apps/kubernetes/tests/fixtures/containerd-2x.toml
  • packages/apps/kubernetes/tests/containerd-config-path-test.sh
  • packages/apps/kubernetes/tests/kubelet-reservation_test.yaml
✅ Files skipped from review due to trivial changes (10)
  • packages/apps/kubernetes/images/kubectl.tag
  • packages/system/capi-providers-bootstrap/files/metadata-talos.yaml
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji.tag
  • packages/apps/kubernetes/images/talos-csr-signer/Dockerfile
  • packages/apps/kubernetes/tests/mhc_rendering_test.yaml
  • packages/core/platform/values.yaml
  • packages/system/capi-providers-cpprovider/files/metadata.yaml
  • packages/apps/kubernetes/README.md
  • api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
  • packages/apps/kubernetes/tests/kubelet_reservation_test.yaml
🚧 Files skipped from review as they are similar to previous changes (28)
  • packages/system/capi-providers-cpprovider/templates/providers.yaml
  • packages/apps/kubernetes/images/talos-csr-signer.tag
  • packages/system/capi-providers-bootstrap/templates/providers.yaml
  • .github/workflows/pull-requests.yaml
  • packages/apps/kubernetes/tests/talos_k8s_support_matrix_test.yaml
  • Makefile
  • packages/system/capi-providers-cpprovider/templates/configmaps.yaml
  • packages/apps/kubernetes/templates/_helpers.tpl
  • pkg/config/config_test.go
  • packages/system/capi-providers-bootstrap/templates/configmaps.yaml
  • packages/apps/kubernetes/tests/gpu_node_labels_test.yaml
  • packages/apps/kubernetes/tests/talos_templates_test.yaml
  • pkg/registry/apps/application/rest.go
  • packages/apps/kubernetes/values.schema.json
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile
  • pkg/config/config.go
  • packages/apps/kubernetes/templates/helmreleases/cilium.yaml
  • packages/apps/kubernetes/values.yaml
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/patches/0001-backport-additional-service-ports-pr347.diff
  • pkg/cmd/server/start.go
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • packages/core/platform/images/migrations/migrations/44
  • packages/apps/kubernetes/templates/helmreleases/ouroboros.yaml
  • packages/core/platform/images/migrations/migrations/45
  • packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml
  • packages/system/capi-providers-bootstrap/patches/0001-rename-default-sa-to-cabpt-manager.diff
  • api/apps/v1alpha1/kubernetes/types.go
  • packages/apps/kubernetes/tests/cluster_test.yaml

Comment on lines +75 to +78
- apiGroups: ["controlplane.cluster.x-k8s.io"]
resources: ["kamajicontrolplanes"]
resourceNames: ["{{ .Release.Name }}"]
verbs: ["get", "patch"]

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking ownerReference and RBAC in talos-reconcile hook..."
rg -n 'blockOwnerDeletion:\s*true|resources:\s*\["kamajicontrolplanes"\]|verbs:\s*\["get",\s*"patch"\]' \
  packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml -n -C2

Repository: cozystack/cozystack

Length of output: 595


🏁 Script executed:

cat -n packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml | sed -n '50,100p'

Repository: cozystack/cozystack

Length of output: 2357


🏁 Script executed:

cat -n packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml | sed -n '360,385p'

Repository: cozystack/cozystack

Length of output: 1339


🏁 Script executed:

rg -n 'talosconfigtemplates' packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml -B2 -A2

Repository: cozystack/cozystack

Length of output: 244


🌐 Web query:

Kubernetes blockOwnerDeletion permission requirements admission controller

💡 Result:

When the OwnerReferencesPermissionEnforcement admission controller is enabled, it enforces specific permission requirements for setting blockOwnerDeletion on an object's ownerReferences [1][2]. To successfully set blockOwnerDeletion to true on an owner reference, the user (or service account) must have update permission on the finalizers subresource of the referenced owner object [1][3][4]. Specifically, the required RBAC rule typically looks like this: - apiGroups: - "the-api-group-of-the-owner" resources: - "owner-resource-name/finalizers" verbs: - "update" If this permission is missing, requests to create or update objects with blockOwnerDeletion set will fail with a forbidden error, often stating: "cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on" [5][6][7]. This requirement ensures that only authorized entities can configure garbage collection behavior that could potentially delay the deletion of owner resources [8][9].

Citations:


🌐 Web query:

Kubernetes ownerReference blockOwnerDeletion update patch permission validation

💡 Result:

In Kubernetes, the blockOwnerDeletion field within an OwnerReference controls whether a dependent object blocks the deletion of its owner during foreground cascading deletion [1][2][3]. To protect owner resources from unauthorized deletion delays, the Kubernetes OwnerReferencesPermissionEnforcement admission plugin validates changes to this field [4][1][5]. When a user (or controller) attempts to set blockOwnerDeletion to true for an ownerReference, the plugin verifies that the subject has specific update permissions on the owner object [4][2][5]. Specifically, the required permission is the update verb on the finalizers subresource of the referenced owner object [4][2][6]. If the subject lacks this permission, the API server will reject the request with a "forbidden" error [4][7][8]. For developers building operators, this requirement is frequently encountered when using controller-runtime's SetControllerReference or similar utilities, which default blockOwnerDeletion to true [2][8]. If an operator fails to include the necessary RBAC permissions for the owner's finalizers, it will be unable to set this field, often resulting in errors such as "cannot set blockOwnerDeletion if an ownerReference refers to a resource you can’t set finalizers on" [2][8]. In Go-based operators using Kubebuilder, this is typically resolved by adding the following RBAC marker to the code and regenerating manifests [2][8]: // +kubebuilder:rbac:groups=,resources=/finalizers,verbs=update

Citations:


Missing update permission on finalizers blocks blockOwnerDeletion: true via admission controller

The ServiceAccount lacks the required permission to set blockOwnerDeletion: true. Kubernetes' OwnerReferencesPermissionEnforcement admission controller requires update verb on the owner's finalizers subresource. This causes the hook Job to fail when creating TalosConfigTemplate, blocking worker reconciliation.

Add the missing RBAC rule:

Required fix
  - apiGroups: ["controlplane.cluster.x-k8s.io"]
    resources: ["kamajicontrolplanes"]
    resourceNames: ["{{ .Release.Name }}"]
    verbs: ["get", "patch"]
+ - apiGroups: ["controlplane.cluster.x-k8s.io"]
+   resources: ["kamajicontrolplanes/finalizers"]
+   resourceNames: ["{{ .Release.Name }}"]
+   verbs: ["update"]

Also applies to: 368-375

🤖 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/apps/kubernetes/templates/talos/talos-reconcile-job.yaml` around
lines 75 - 78, The RBAC rule for kamajicontrolplanes in the talos-reconcile-job
ServiceAccount is missing the required `update` verb on the `finalizers`
subresource. When the Job attempts to create TalosConfigTemplate with
blockOwnerDeletion set to true, Kubernetes' OwnerReferencesPermissionEnforcement
admission controller enforces that the ServiceAccount must have update
permissions on the owner resource's finalizers. Add a new RBAC rule with
apiGroup controlplane.cluster.x-k8s.io, resources
kamajicontrolplanes/finalizers, resourceNames matching the release name, and
verbs containing only update. This same pattern needs to be applied to the other
kamajicontrolplanes-related rules mentioned in the also applies to section.

@myasnikovdaniil myasnikovdaniil self-assigned this Jun 23, 2026
Andrei Kvapil (kvaps) and others added 9 commits June 23, 2026 14:30
…los v0.6.12

Vendor CABPT v0.6.12 as a second BootstrapProvider alongside the existing kubeadm one. The default ServiceAccount is renamed to cabpt-manager via a patch on the upstream bundle.

Co-authored-by: IvanHunters <xorokhotnikov@gmail.com>
Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
…r (v0.19.0 + additionalServicePorts backport)

Rebuild the Kamaji control-plane provider from upstream v0.19.0 with a backport of additionalServicePorts (upstream PR #347), exposing KamajiControlPlane.spec.network.additionalServicePorts so trustd (50001/TCP) can be published on the apiserver Service. Image is built from source and pinned by digest; the 0.17-0.19 release series is registered in metadata.

Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Generate Talos PKI (Ed25519 CA + trustd TLS) via cert-manager and stable random Talos secrets (token, clusterId, clusterSecret, bootstrapToken) using the helm lookup-and-reuse pattern. Build the talos-csr-signer sidecar from a pinned source commit, digest-pinned, with resources and securityContext.

Co-authored-by: IvanHunters <xorokhotnikov@gmail.com>
Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Switch tenant worker bootstrap from Ubuntu+kubeadm to Talos. MachineDeployment.bootstrap moves from KubeadmConfigTemplate to a TalosConfigTemplate produced by a post-install/upgrade reconcile hook, which also materialises the kubeadm-format bootstrap-token Secret in the tenant kube-system. Workers boot from the Talos openstack image via a CDI DataVolume; the system disk is virtio-blk with logical=512/physical=4096 so 4Ki-native backends (LINSTOR/DRBD) and SeaBIOS both work; the separate kubelet disk is dropped. Pod/service CIDRs, dnsDomain, controlPlane endpoint and cilium k8sServiceHost are pinned to the tenant ranges / Kamaji apiserver ClusterIP to survive the DNS chicken-and-egg at bootstrap. Adds a Talos<->Kubernetes support-matrix fail() guard and drops v1.30. Removes the dead ubuntu-container-disk build chain and the orphaned copy-patch-containerd template.

Co-authored-by: IvanHunters <xorokhotnikov@gmail.com>
Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Parse a new release.cozystack.io/helm-install-disable-wait annotation in start.go and apply it as Install/Upgrade.DisableWait in rest.go, so helm-controller no longer blocks post-install hooks on in-tenant addon readiness. This breaks the reconcile-hook chicken-and-egg where the hook that creates the worker TalosConfigTemplate was gated behind addons that need workers to schedule. Covered by a config table test.

Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Migration 44 pins Helm-managed KubeadmConfigTemplates with resource-policy=keep so Helm cannot prune them mid-rollover; migration 45 bumps tenants on spec.version v1.30 to v1.31 ahead of the Talos support-matrix guard. Both stamp cozystack-version with its deletion-protection label. targetVersion is bumped to 46.

Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Add helm-unittest suites for the new Talos templates: MHC maxUnhealthy int-vs-percent rendering, the Talos<->Kubernetes support-matrix guard, the talos templates, and the ported kubelet-reservation math; restore the gpu=on node-label assertions. Remove the obsolete containerd-config-path test and fixtures. Raise tenant control-plane wait timeouts in the e2e runner for Talos PKI + sidecar boot.

Co-authored-by: Andrei Kvapil <kvapss@gmail.com>
Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Use git diff --binary for the PR patch artifact so vendored binary provider bundles (components*.gz) are reviewable.

Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
- talos-reconcile-job: emit a `---` separator inside the nodeGroups
  range so a tenant with multiple node groups renders valid
  multi-document YAML instead of two Jobs concatenated into one doc
- talos-reconcile + bootstrap-token jobs: add a restricted
  securityContext (drop ALL caps, no privilege escalation, read-only
  root fs) and CPU/memory requests+limits; drop the unused management
  SA token mount on the bootstrap-token job, which only talks to the
  tenant apiserver via the mounted admin kubeconfig
- migration 44: list Helm-managed resources by the
  app.kubernetes.io/managed-by=Helm label, not meta.helm.sh/release-name
  (an annotation, so it never matched as a --selector — the keep
  annotation was silently never applied)
- cluster.yaml: fail fast on a malformed nodeHealthCheck.maxUnhealthy
  rather than silently coercing it to 0 via `| int`
- config: make helm-install-disable-wait parsing genuinely
  case-insensitive (EqualFold + trim) to match its documented contract
- capi-providers bootstrap/cpprovider Makefiles: guard the
  patches/*.diff loop against an empty glob

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil myasnikovdaniil force-pushed the phase1-talos-migration branch from 1ecbf37 to b5cc2ae Compare June 23, 2026 11:37

@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

♻️ Duplicate comments (1)
packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml (1)

75-78: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Missing update on kamajicontrolplanes/finalizers for blockOwnerDeletion: true.

The applied TalosConfigTemplate (Line 374) sets blockOwnerDeletion: true on its owner ref. With the OwnerReferencesPermissionEnforcement admission plugin enabled, the ServiceAccount must hold update on the owner's finalizers subresource, or the kubectl apply is rejected and worker reconciliation stalls.

Required RBAC rule
   - apiGroups: ["controlplane.cluster.x-k8s.io"]
     resources: ["kamajicontrolplanes"]
     resourceNames: ["{{ .Release.Name }}"]
     verbs: ["get", "patch"]
+  - apiGroups: ["controlplane.cluster.x-k8s.io"]
+    resources: ["kamajicontrolplanes/finalizers"]
+    resourceNames: ["{{ .Release.Name }}"]
+    verbs: ["update"]
🤖 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/apps/kubernetes/templates/talos/talos-reconcile-job.yaml` around
lines 75 - 78, The RBAC rule for kamajicontrolplanes is missing the update
permission on the finalizers subresource, which is required when
blockOwnerDeletion is set to true on owner references. Add a new rule after the
existing kamajicontrolplanes rule that specifies apiGroups as
controlplane.cluster.x-k8s.io, resources as kamajicontrolplanes/finalizers,
resourceNames as {{ .Release.Name }}, and verbs as update. This will allow the
ServiceAccount to modify the finalizers subresource, preventing the
OwnerReferencesPermissionEnforcement admission plugin from rejecting the kubectl
apply operation.
🧹 Nitpick comments (1)
packages/system/capi-providers-bootstrap/Makefile (1)

29-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add --fail to the upstream curl download.

curl -sSL exits 0 on HTTP 4xx/5xx (e.g., a renamed/removed release asset), writing the error page into bootstrap-components.yaml. The build then proceeds with a corrupt input instead of failing at the download step. -f makes the failure explicit.

♻️ Fail fast on download errors
-	curl -sSL "https://github.com/siderolabs/cluster-api-bootstrap-provider-talos/releases/download/$(CABPT_UPSTREAM_VERSION)/bootstrap-components.yaml" \
+	curl -fsSL "https://github.com/siderolabs/cluster-api-bootstrap-provider-talos/releases/download/$(CABPT_UPSTREAM_VERSION)/bootstrap-components.yaml" \
 		-o .update-tmp/bootstrap-components.yaml
🤖 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/capi-providers-bootstrap/Makefile` around lines 29 - 30, The
curl command downloading bootstrap-components.yaml from the GitHub release is
missing the --fail flag, which means it will silently succeed even on HTTP
4xx/5xx errors and write the error page to the file instead of failing the
build. Add the --fail (or -f) flag to the curl -sSL command to make curl exit
with a non-zero status when the download fails, ensuring the build fails fast at
the download step rather than proceeding with corrupted input.
🤖 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/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml`:
- Around line 85-90: The securityContext configuration in the
bootstrap-token-tenant-job.yaml file is incomplete and missing three security
fields that are present in the sibling talos-csr-signer container defined in
cluster.yaml. In the securityContext block that currently contains
allowPrivilegeEscalation, capabilities drop, and readOnlyRootFilesystem, add the
missing fields runAsNonRoot, runAsUser, and seccompProfile by referencing the
corresponding values from the talos-csr-signer container in cluster.yaml to
ensure consistency and compliance with Kubernetes restricted Pod Security
Standard.

In `@packages/apps/kubernetes/templates/talos/talos-pki.yaml`:
- Around line 67-72: In the talos-pki.yaml file, locate the certificate
configuration with the Ed25519 algorithm. The usages list currently contains
three items including "key encipherment", but Ed25519 is a signature-only
algorithm that does not support key encipherment operations. Remove the "key
encipherment" entry from the usages list, leaving only "digital signature" and
"server auth" as the valid usages for an Ed25519-based TLS certificate.

---

Duplicate comments:
In `@packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml`:
- Around line 75-78: The RBAC rule for kamajicontrolplanes is missing the update
permission on the finalizers subresource, which is required when
blockOwnerDeletion is set to true on owner references. Add a new rule after the
existing kamajicontrolplanes rule that specifies apiGroups as
controlplane.cluster.x-k8s.io, resources as kamajicontrolplanes/finalizers,
resourceNames as {{ .Release.Name }}, and verbs as update. This will allow the
ServiceAccount to modify the finalizers subresource, preventing the
OwnerReferencesPermissionEnforcement admission plugin from rejecting the kubectl
apply operation.

---

Nitpick comments:
In `@packages/system/capi-providers-bootstrap/Makefile`:
- Around line 29-30: The curl command downloading bootstrap-components.yaml from
the GitHub release is missing the --fail flag, which means it will silently
succeed even on HTTP 4xx/5xx errors and write the error page to the file instead
of failing the build. Add the --fail (or -f) flag to the curl -sSL command to
make curl exit with a non-zero status when the download fails, ensuring the
build fails fast at the download step rather than proceeding with corrupted
input.
🪄 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: 0c442fd7-7d02-4da6-8c77-945e8aaca765

📥 Commits

Reviewing files that changed from the base of the PR and between 1ecbf37 and b5cc2ae.

⛔ Files ignored due to path filters (2)
  • packages/system/capi-providers-bootstrap/files/components-talos.gz is excluded by !**/*.gz
  • packages/system/capi-providers-cpprovider/files/components.gz is excluded by !**/*.gz
📒 Files selected for processing (61)
  • .github/workflows/pull-requests.yaml
  • Makefile
  • api/apps/v1alpha1/kubernetes/types.go
  • api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
  • hack/e2e-apps/run-kubernetes.sh
  • packages/apps/kubernetes/Makefile
  • packages/apps/kubernetes/README.md
  • packages/apps/kubernetes/files/versions.yaml
  • packages/apps/kubernetes/images/kubectl.tag
  • packages/apps/kubernetes/images/talos-csr-signer.tag
  • packages/apps/kubernetes/images/talos-csr-signer/Dockerfile
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk/Dockerfile
  • packages/apps/kubernetes/templates/_helpers.tpl
  • packages/apps/kubernetes/templates/cluster.yaml
  • packages/apps/kubernetes/templates/copy-patch-containerd.yaml
  • packages/apps/kubernetes/templates/helmreleases/cilium.yaml
  • packages/apps/kubernetes/templates/helmreleases/ouroboros.yaml
  • packages/apps/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-pki.yaml
  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml
  • packages/apps/kubernetes/templates/talos/talos-secrets.yaml
  • packages/apps/kubernetes/tests/cluster_test.yaml
  • packages/apps/kubernetes/tests/containerd-config-path-test.sh
  • packages/apps/kubernetes/tests/fixtures/containerd-1x.toml
  • packages/apps/kubernetes/tests/fixtures/containerd-2x.toml
  • packages/apps/kubernetes/tests/gpu_node_labels_test.yaml
  • packages/apps/kubernetes/tests/kubelet-reservation_test.yaml
  • packages/apps/kubernetes/tests/kubelet_reservation_test.yaml
  • packages/apps/kubernetes/tests/mhc_rendering_test.yaml
  • packages/apps/kubernetes/tests/talos_k8s_support_matrix_test.yaml
  • packages/apps/kubernetes/tests/talos_templates_test.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/apps/kubernetes/values.yaml
  • packages/core/platform/images/migrations/migrations/45
  • packages/core/platform/images/migrations/migrations/46
  • packages/core/platform/values.yaml
  • packages/system/capi-providers-bootstrap/Makefile
  • packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml
  • packages/system/capi-providers-bootstrap/files/metadata-talos.yaml
  • packages/system/capi-providers-bootstrap/patches/0001-rename-default-sa-to-cabpt-manager.diff
  • packages/system/capi-providers-bootstrap/templates/configmaps.yaml
  • packages/system/capi-providers-bootstrap/templates/providers.yaml
  • packages/system/capi-providers-cpprovider/Makefile
  • packages/system/capi-providers-cpprovider/files/control-plane-components.yaml
  • packages/system/capi-providers-cpprovider/files/metadata.yaml
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji.tag
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/patches/0001-backport-additional-service-ports-pr347.diff
  • packages/system/capi-providers-cpprovider/templates/configmaps.yaml
  • packages/system/capi-providers-cpprovider/templates/providers.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • pkg/cmd/server/start.go
  • pkg/config/config.go
  • pkg/config/config_test.go
  • pkg/registry/apps/application/rest.go
💤 Files with no reviewable changes (13)
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag
  • packages/apps/kubernetes/templates/copy-patch-containerd.yaml
  • packages/apps/kubernetes/files/versions.yaml
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag
  • packages/apps/kubernetes/tests/fixtures/containerd-1x.toml
  • packages/apps/kubernetes/tests/containerd-config-path-test.sh
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag
  • packages/apps/kubernetes/tests/kubelet-reservation_test.yaml
  • packages/apps/kubernetes/tests/fixtures/containerd-2x.toml
  • packages/apps/kubernetes/images/ubuntu-container-disk/Dockerfile
  • packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag
✅ Files skipped from review due to trivial changes (7)
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji.tag
  • packages/system/capi-providers-bootstrap/files/metadata-talos.yaml
  • packages/apps/kubernetes/images/talos-csr-signer/Dockerfile
  • packages/apps/kubernetes/images/kubectl.tag
  • Makefile
  • api/apps/v1alpha1/kubernetes/zz_generated.deepcopy.go
  • packages/apps/kubernetes/README.md
🚧 Files skipped from review as they are similar to previous changes (28)
  • packages/apps/kubernetes/tests/talos_k8s_support_matrix_test.yaml
  • packages/apps/kubernetes/images/talos-csr-signer.tag
  • packages/apps/kubernetes/templates/helmreleases/cilium.yaml
  • packages/system/capi-providers-cpprovider/files/metadata.yaml
  • packages/apps/kubernetes/tests/mhc_rendering_test.yaml
  • .github/workflows/pull-requests.yaml
  • packages/apps/kubernetes/templates/_helpers.tpl
  • pkg/config/config_test.go
  • packages/system/capi-providers-cpprovider/templates/providers.yaml
  • packages/apps/kubernetes/tests/talos_templates_test.yaml
  • packages/apps/kubernetes/templates/helmreleases/ouroboros.yaml
  • pkg/cmd/server/start.go
  • packages/system/capi-providers-bootstrap/templates/providers.yaml
  • packages/apps/kubernetes/tests/gpu_node_labels_test.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/system/capi-providers-bootstrap/patches/0001-rename-default-sa-to-cabpt-manager.diff
  • packages/system/capi-providers-bootstrap/templates/configmaps.yaml
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/patches/0001-backport-additional-service-ports-pr347.diff
  • pkg/config/config.go
  • api/apps/v1alpha1/kubernetes/types.go
  • pkg/registry/apps/application/rest.go
  • packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile
  • packages/system/capi-providers-cpprovider/templates/configmaps.yaml
  • packages/apps/kubernetes/values.yaml
  • packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml
  • packages/apps/kubernetes/tests/kubelet_reservation_test.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
  • packages/apps/kubernetes/tests/cluster_test.yaml

Comment thread packages/apps/kubernetes/templates/talos/talos-pki.yaml

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 — Talos worker bootstrap via CABPT is correct and well-guarded. Every substantive automated-reviewer finding is already addressed in the head commit, and the three "critical/P1" items are false positives I verified against source. The remaining items are minor and non-blocking.

Business context: Replace tenant-worker bootstrap from Ubuntu+kubeadm to Talos (driven by cluster-api-bootstrap-provider-talos plus a talos-csr-signer sidecar in the Kamaji control-plane pod), so tenant clusters get immutable, lifecycle-managed workers while existing tenants roll over automatically.

Blockers

None.

Verified and dropped (automated-reviewer false positives)

I checked these against source before dropping them, so they don't get re-litigated:

  • "Go 1.26 does not exist" (Dockerfiles) — stale. golang:1.26 is current and is the repo's standard builder tag. Evidence: same tag used across first-party images on main. No change needed.
  • "base64 cert must be PEM-decoded before injection" (talos-reconcile-job.yaml:420,479) — incorrect for Talos. machine.ca.crt / cluster.ca.crt in a Talos machineconfig expect base64-encoded PEM, which is exactly what the secret .data.tls.crt / .data.ca.crt already hold; the _B64 variables are injected verbatim on a single line. Evidence: talos-reconcile-job.yaml:319-320 reads .data.* (already base64) and :420,479 inject it unmodified — correct.
  • "the migration annotation will not stop Helm pruning the KubeadmConfigTemplate" — incorrect. Helm's delete path calls info.Get() to refresh each prune candidate from the live object before reading helm.sh/resource-policy, and the live object carries the annotation the migration sets. Evidence: helm.sh/helm/v3 pkg/kube/client.go Update() lines 436-448 (info.Get() then annotations[ResourcePolicyAnno] == KeepPolicy -> skip delete).

Non-blocking follow-ups

  1. kubectl pin vs tenant skew — images/kubectl.tag pins alpine/k8s:1.33.4, but the bootstrap-token Job runs it against tenant apiservers spanning v1.31-v1.35 (±2 minors at the edges, outside the documented skew window). The calls are basic GA CRUD (Secret + rbac/v1 apply, delete) and work in practice, so this is hardening rather than a break; consider tracking the tenant range or using a per-tenant kubectl. Evidence: packages/apps/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml:60,100-116.
  2. Migration 45 is committed non-executable (100644) while siblings 44/46 are 100755. Harmless because run-migrations.sh runs chmod +x before exec, but it's an avoidable inconsistency. Evidence: git ls-files --stage packages/core/platform/images/migrations/migrations/{44,45,46}; run-migrations.sh chmod line.
  3. blockOwnerDeletion RBAC — the talos-reconcile Role grants get/patch on kamajicontrolplanes but not update on kamajicontrolplanes/finalizers, while the applied TalosConfigTemplate sets blockOwnerDeletion: true. This only fails if the management apiserver enables the (non-default) OwnerReferencesPermissionEnforcement admission plugin; add the finalizers/update rule defensively if there's any chance of enabling it. Evidence: talos-reconcile-job.yaml:75-78,374.
  4. CABPT metrics Service targetPort: https does not match the controller's named container port metrics (8443), so metrics scraping won't route. This lives in the vendored upstream bundle, so the fix belongs upstream rather than as a local edit. Evidence: packages/system/capi-providers-bootstrap/files/bootstrap-components-talos.yaml:654 vs :716-717.
  5. Ed25519 TLS cert lists key encipherment usage. RFC 9295 says Ed25519 certs MUST NOT carry keyEncipherment; harmless for the signer's TLS 1.3 listener but worth dropping for correctness. Evidence: packages/apps/kubernetes/templates/talos/talos-pki.yaml:67-72.
  6. Migration comments reference "PR #2610" in shipped files. Internal PR provenance in committed code is best avoided; describe the change, not where it was tracked. Evidence: migrations/45:4-7, migrations/46:5.

Overlap with the other open PR on the same branch

A second PR with the identical title and the same phase1-talos-migration branch name is still open and is superseded by this one: this PR is that work rebased onto main (to sit on top of the kube-controller-manager VAP fix), with original authorship preserved and history squashed. Two things to note: the PR body's "byte-identical to the other head — no code changes" is now stale, since this head carries an extra fix: address review findings commit (head b5cc2ae) plus a CI commit beyond that tree; and leaving both open races the same branch name. Evidence: git log --oneline shows b5cc2ae fix(kubernetes): address review findings ... on top of the eight squashed commits. Recommend closing the older PR in favor of this one.

Scope verified

Helm prune semantics (against helm source), CABPT TalosConfigTemplate apiVersion (v1alpha3 is served + storage in the vendored CRD), Go disable-wait wiring (Install/Upgrade always non-nil so no nil-deref; invalid annotation fails startup loudly), migration chain (44->45->46->47, targetVersion 47, both new stamps carry the platform.cozystack.io/no-delete label), MHC maxUnhealthy rendering (validates int-or-percent and fails on garbage), README/schema/types regenerated and in sync.

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review: Request changes

Four MAJOR findings below block merge for the "existing tenants keep working without manual intervention" claim. Minors are listed separately and can be addressed in a follow-up.

MAJOR

1. Air-gapped tenants lose self-healing; <release>-patch-containerd Secret pruned, MHC default flipped to enable remediation

Three changes interact and break tenants that run with _cluster.registries.mirrors set:

  1. packages/apps/kubernetes/templates/copy-patch-containerd.yaml is deleted. The <release>-patch-containerd Secret it rendered is not keep-annotated by migration 45 (only KubeadmConfigTemplate and KubeadmConfig are). On chart upgrade Helm prunes the Secret, leaving the still-extant kubeadm-backed MachineSet referencing a missing input.
  2. nodeHealthCheck.maxUnhealthy default flipped from 0 (integer literal in old cluster.yaml:591) to "50%" (string in new values.yaml:307). The type change is itself a schema break — tenants that previously overrode maxUnhealthy: 0 will now fail render with the new ^[0-9]+$|^[0-9]+%$ regex guard at cluster.yaml:614-622. The default change enables MHC remediation where it was previously disabled.
  3. MachineDeployment bootstrap ref switches to TalosConfigTemplate and worker images come from factory.talos.dev via CDI. For air-gapped tenants the DataVolume fetch hangs.

Combined scenario: previously-stable tenant fleet, transient NotReady on one Ubuntu worker (containerd hiccup, kernel softlockup) → MHC now remediates (didn't before) → CAPI provisions a Talos Machine → factory.talos.dev egress blocked → Machine stuck in Provisioning → nodeStartupTimeout (10m) → rolling update stalls. The migration 45 inline comment explicitly acknowledges this: "In pathological cases (slow Talos image fetch from factory.talos.dev, MachineHealthCheck remediation, etc.) workers can hang in pending state with nothing to bootstrap from." — the mitigation (keep-annotate KCT only) is incomplete.

Required fixes:

  • Either keep-annotate <release>-patch-containerd in migration 45 alongside KCT, OR render-time fail() in the chart when _cluster.registries.mirrors is non-empty and no images.talosInstaller override is set, so air-gapped tenants get a clear render-time error instead of a partially broken cluster.
  • Add a "Breaking Changes" entry in packages/apps/kubernetes/README.md documenting the MHC default flip (0"50%") and the type change (integer → string). Recommend operators pin nodeHealthCheck.maxUnhealthy: "0%" before upgrade if they want legacy behavior.

2. README documents unsupported Kubernetes versions

packages/apps/kubernetes/README.md:15 says "Users can select the supported patch versions from 1.30 to 1.35." but the schema enum in values.schema.json is ["v1.31","v1.32","v1.33","v1.34","v1.35"] and templates/_versions.tpl has a fail() guard for v1.30. An operator following the README to create a fresh tenant with version: v1.30 gets a CRD admission rejection. Update the README to from 1.31 to 1.35 and add a Breaking Changes entry pointing at migration 46.

3. talos-reconcile-job 10-minute polling window is tight for slow cold-starts; chart upgrade fails with no automatic recovery

packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml:317 polls for i in $(seq 1 120); do ... sleep 5; done = 600s. The Job blocks on (a) Kamaji apiserver Service ClusterIP allocation, (b) cert-manager CA Secret issuance, and (c) KamajiControlPlane apiserver readiness. On clusters with slow image pulls or cert-manager cold-start the 10-minute window is insufficient. backoffLimit: 5 + restartPolicy: OnFailure gives ~30 minutes of total retries before permanent fail; the HR ends in Failed state and requires manual kubectl delete job to retrigger.

Pre-PR the chart relied on a lookup race that silently produced empty SANs but didn't gate Helm readiness on it. This PR puts the reconcile-job on the critical path, regressing operator experience for slow clusters.

Fix: widen the per-Job polling window to ~25min (inside the 20min HR install timeout × backoff factor), or document the manual retrigger procedure in the README.

4. GitOps-managed tenant CRs with version: v1.30 in Git get reverted by Flux after migration 46 patches them

Migration 46 patches kuberneteses.apps.cozystack.io spec.version on the live CR from v1.30 to v1.31. For operators using Flux/Argo to manage tenant CRs from Git, the next reconcile will revert the field back to v1.30, hitting the new _versions.tpl fail() guard and breaking the HR after the platform upgrade has already completed.

This is not a code bug but a deployment-flow gap. The PR body claim "existing tenants keep working without manual intervention" does not hold for GitOps-managed clusters.

Fix: add an explicit operator-action note in the release-note block: "Operators managing tenant kubernetes.apps.cozystack.io CRs from Git MUST update spec.version: v1.30 to v1.31 in their source repository before bumping the platform to v1.5.0, otherwise the next GitOps reconcile will revert the migration."

MINOR

  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml:496-501 and templates/talos/bootstrap-token-tenant-job.yaml:85-90 — hook pods miss runAsNonRoot: true / runAsUser. The talos-csr-signer sidecar at cluster.yaml:294-303 already sets these. Mirror the pattern; otherwise the chart fails under PodSecurity restricted enforcement on tenant namespaces.
  • packages/apps/kubernetes/templates/talos/talos-secrets.yaml:36-53 — when lookup finds an existing <release>-talos-secrets Secret missing the bootstrapTokenId / bootstrapTokenSecret keys (early-Talos-branch tenants), the chart randomizes new ones and leaves the previous bootstrap-token-<old-id> Secret orphaned in tenant kube-system. Add an else branch that reconstructs the split keys from the old combined value when present.
  • packages/apps/kubernetes/templates/talos/talos-reconcile-job.yaml:337-341 — JSON merge patch on KamajiControlPlane.spec.network.certSANs uses array-replace semantics; any operator-added SANs are wiped on every chart upgrade. Either document the chart-managed contract or switch to a JSON Patch add op.
  • packages/core/platform/images/migrations/migrations/46 — exit 0 on partial failure with a ConfigMap annotation as the only signal. Retry-on-next-upgrade semantics are intentional per the inline comment, but consider emitting a kubectl create event so operators get a dashboard-visible signal instead of having to kubectl describe cm cozystack-version.
  • packages/apps/kubernetes/templates/talos/talos-pki.yaml:76-80 — comment claims spec.ipAddresses is patched in by the reconcile hook; no such patch exists. Remove or rewrite.
  • packages/core/platform/images/migrations/migrations/45:96-97 — header comment says the migration annotates "KubeadmConfig instances spawned from them" but the app.kubernetes.io/managed-by=Helm selector doesn't match CAPI-spawned instances. Update the comment to reflect actual semantics (KubeadmConfigTemplate only).
  • packages/apps/kubernetes/templates/cluster.yaml:94hasKey .group "ephemeralStorage" is dead code (CRD field-pruning strips the unknown field before Helm sees it). Remove or update the error message to clarify it only fires for manually-injected chart values.

Caveats

  • Five prior reviews on this PR were intentionally not read during this independent pass; findings above may overlap with earlier discussion.
  • kubernetes-<release>-ingress-nginx Service/Ingress in cozyrds services.include / ingresses.include has no matching resourceNames grant in templates/dashboard-resourcemap.yaml. This is pre-existing on main, not introduced by this PR — listed only as context for a future cleanup.

…n-bump caveat

Document two behaviour changes from the Phase 1 Talos rollover in the package
Breaking Changes: the worker MachineHealthCheck maxUnhealthy moved from a
hard-coded 0 (remediation effectively off) to nodeHealthCheck.maxUnhealthy
defaulting to "50%" (remediation enabled), and that GitOps-managed tenant
Kubernetes CRs must bump spec.version to v1.31 in Git or migration 46's patch is
reverted on the next source reconcile. Also fix the supported version range in
the intro (1.31-1.35; v1.30 was removed from the support matrix).

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Add runAsNonRoot/runAsUser/seccompProfile to the bootstrap-token and
talos-reconcile hook Jobs so they satisfy PodSecurity "restricted" (matching the
csr-signer sidecar). Drop the keyEncipherment usage from the Ed25519 trustd
Certificate (RFC 9295 - Ed25519 keys cannot do key encipherment). Correct the
stale talos-pki comment that claimed the reconcile hook patches spec.ipAddresses:
the cert carries DNS SANs only and workers route to trustd via the Service DNS
name, as the reconcile job's own routing note explains.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
…refs

Set the executable bit on migration 45 to match its siblings (44, 46). Replace
"PR #2610" references in migrations 45/46 with a description of the change.
Correct migration 45's comment: the KubeadmConfig children CAPI spawns from the
template are owned by their Machine (not Helm), so they carry no
app.kubernetes.io/managed-by=Helm label, are not matched by the selector, and
are never at risk of Helm pruning.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil

Copy link
Copy Markdown
Contributor Author

IvanHunters thanks for the thorough pass. Addressed in 3478ed3 / a4c6a07 / bf92f3f:

Fixed

  • MINOR#1 (hook-pod securityContext) — added runAsNonRoot: true / runAsUser: 65532 / seccompProfile: RuntimeDefault to bootstrap-token-tenant-job and talos-reconcile-job, matching the csr-signer sidecar (PodSecurity restricted).
  • MINOR#5 (talos-pki comment) — rewritten: the cert carries DNS SANs only and is never patched. Verified against talos-reconcile-job.yaml's own routing note ("the Talos PKI Certificate is issued with DNS SANs at chart render time and never needs to be patched"); workers reach trustd via the Service DNS name with the ClusterIP injected through machine.network.extraHostEntries.
  • MINOR#6 (migration 45 comment) — corrected: CAPI-spawned KubeadmConfig children are Machine-owned, carry no managed-by=Helm label, so they're neither matched by the selector nor at risk of Helm pruning.
  • MAJOR#2 (stale version range) — fixed README.md "1.30 to 1.35" → "1.31 to 1.35".
  • MAJOR#1 / MAJOR#4 (documentation) — added Breaking-Changes notes for (a) the worker MHC maxUnhealthy default moving from a hard-coded 0 (remediation off) to "50%" (remediation enabled), with the "0%" opt-out, and (b) the GitOps caveat: a Git-reconciled tenant Kubernetes CR must bump spec.version to v1.31, or migration 46's patch is reverted on the next source reconcile.
  • lexfrei follow-ups: migration 45 is now executable (0755), Ed25519 cert drops the invalid keyEncipherment usage (RFC 9295), and the "PR feat(kubernetes): bootstrap tenant workers with Talos via CABPT #2610" provenance is scrubbed from the shipped migration comments.

Declined, with reasoning

  • MAJOR#2 as "breaking, needs manual action" — migration 46 transparently bumps live v1.30 tenants to v1.31; API/dashboard-managed tenants need no action. The only real edge is the Git-managed CR case above, which is now documented.
  • MAJOR#3 (600s poll "too tight") — no evidence the window is hit. It's the same 10-minute budget your own e2e commit validated (run-kubernetes.sh:148-156, "sits well inside helm-install-timeout: 20m"), with backoffLimit + the 20m HR ceiling on top. feat(kubernetes): bootstrap tenant workers with Talos via CABPT #2610 deliberately cut backoffLimit so Flux surfaces failures rather than retrying for hours; widening the window reintroduces that. No change — happy to revisit with a concrete timeout repro.
  • MAJOR#1 keep-annotate *-patch-containerd — the air-gap loss is already documented (README) and is a known Phase-2 follow-up. After the template removal that Secret is orphaned (no consumer in the Talos machineconfig), so Helm pruning it is correct cleanup; keep-annotating would pin dead state. Kept the real sub-point (the MHC flip) as a documented behaviour change above.
  • MINOR#2 (orphaned bootstrap token)talos-secrets.yaml is new on this branch; the combined-but-no-split-keys state is only reachable from an intermediate dev build of this branch, not from a released main tenant upgrade (there $existing is nil and all keys are generated together). Not reachable on the upgrade path.
  • MINOR#3 (certSANs array-replace) — intentional and already documented inline (talos-reconcile-job.yaml): Kamaji owns the full SAN list, so a JSON-merge replace is the desired semantics.
  • bot false-positives: golang:1.26 is the repo-standard builder tag (used across cozystack-operator/kamaji/api); and Talos machine.ca.crt expects base64-encoded PEM, so the suggested PEM-decode would break it. No change.

MINOR#4 (emit a migration-46 failure Event) and MINOR#7 (the helm template-only ephemeralStorage guard) I've left as optional follow-ups. Re-requesting review.

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving. The 4 MAJOR / 7 MINOR from my prior pass are addressed across 3478ed3 / a4c6a07 / bf92f3f, either by code, by Breaking-Changes docs, or by a defensible counter-argument:

Closed

  • MAJOR#2 (README v1.30): fixed (1.31–1.35).
  • MAJOR#1/4 (MHC default flip + GitOps spec.version revert): documented as Breaking Changes in packages/apps/kubernetes/README.md:90-95, with the nodeHealthCheck.maxUnhealthy: "0%" opt-out called out.
  • MAJOR#3 (10-min poll window): withdrawn — e2e-validated on a real cluster (hack/e2e-apps/run-kubernetes.sh:148-156), intentional fast-fail design with the 20m HR ceiling, and reverting would mean Flux retries silently for hours instead of surfacing failures.
  • MINOR#1 / #5 / #6: fixed.
  • MINOR#2 (orphan bootstrap-token): only reachable from an intermediate dev build of this branch, not from a released-main upgrade. Withdrawn.
  • MINOR#3 (certSANs JSON-merge): intentional per inline doc; Kamaji owns the SAN list. Withdrawn.

Non-blocking residuals — follow-ups, not merge gates

  1. packages/core/platform/images/migrations/migrations/45:22-24 — comment overstates GC: KubeadmConfigTemplate pinned with helm.sh/resource-policy: keep is not owned by any MachineSet (CAPI references templates by name, not via metadata.ownerReferences). After the Talos rollover Helm leaves them alone, nothing else GCs them, and they sit in tenant namespaces indefinitely. Same applies to the keep-annotated KubeadmConfig children. A follow-up migration that drops the keep-annotated KCT/KubeadmConfig (plus the orphaned <release>-patch-containerd Secret) once the Talos MachineSet has been stable for N reconciles would close out the cleanup story.

  2. Narrow race during the rollover window: keep-annotated KCT still carries contentFrom.secret.name: <release>-patch-containerd, and that Secret is unconditionally pruned on the upgrade (its template was removed). If MHC remediates an old kubeadm-backed Machine BEFORE MachineDeployment finishes scaling the old MachineSet down, the old MachineSet's controller can spawn a replacement from the keep'd KCT and the contentFrom lookup fails → bootstrap hangs. Documented mitigation is nodeHealthCheck.maxUnhealthy: "0%". The follow-up migration in #1 closes this by removing the dangling reference entirely; until then the "0%" opt-out is the safety net.

  3. packages/core/platform/sources/capi-provider-bootstrap-kubeadm.yaml — filename is now misleading since the wrapped package installs both kubeadm and Talos BootstrapProvider. Rename or split per-provider while the area is fresh.

  4. PR body / release-note: "existing tenants keep working without manual intervention" is now technically contradicted by the three Breaking-Changes entries (MHC "0%" opt-out, GitOps spec.version bump, air-gapped Phase-2 wait). Consider softening to "API/dashboard-managed tenants need no action; see Breaking Changes for GitOps and air-gapped operators."

  5. packages/apps/kubernetes/templates/talos/bootstrap-token-tenant-job.yaml creates a namespaced talos:node-apiserver-endpoint-discovery Role + RoleBinding in the tenant default namespace granting the system:nodes group get/list/watch on endpoints / endpointslices. Read-only and tightly scoped, but lands on every kubernetes-chart install/upgrade — worth a line in the tenant RBAC surface so operators auditing tenant clusters know to expect it.

  6. hack/e2e-apps/run-kubernetes.sh:43local port="$3" declared but never used (SC2034). Pre-existing on main, but the change set rewrites enough of this file that dropping it here is low-cost.

  7. Migration 46 failed-tenants signal (already acknowledged as a follow-up): wire the cozystack.io/migration-46-failed-tenants annotation on cozystack-version into a dashboard alert or release-notes runbook entry so operators don't need to kubectl describe cm to find stuck tenants.

@myasnikovdaniil myasnikovdaniil added do-not-merge/hold Indicates that a PR should not merge because someone has issued /hold and removed do-not-merge/hold Indicates that a PR should not merge because someone has issued /hold labels Jun 26, 2026
@myasnikovdaniil myasnikovdaniil merged commit 1df8c7c into main Jun 26, 2026
18 of 19 checks passed
@myasnikovdaniil myasnikovdaniil deleted the phase1-talos-migration branch June 26, 2026 15:06
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Jun 26, 2026
#3118)

## What this PR does

Migrations 45 and 46 (added by #2931) stamp the `cozystack-version`
ConfigMap
with an inline `kubectl apply` heredoc instead of sourcing
`lib/cozystack-version.sh` and calling `stamp_cozystack_version`, the
way
migrations 42/43/44 do. The `cozystack-version-stamp.bats` guard forbids
any
migration ≥42 from bypassing the helper, so `make bats-unit-tests` fails
on
migration 45.

This only surfaces in PR CI (`make unit-tests`) — `main`-push CI does
not run
the bats suite — so #2931 landed red on this test and now blocks every
PR that
merges current `main`.

This routes both stamps through the helper. The helper renders a
byte-identical labeled manifest (golden-pinned in the bats suite), so
the
change is behaviour-preserving: it just restores the no-label-drift
guarantee
the heredoc form could silently lose.

Verified: `make bats-unit-tests` passes (was failing on migration 45),
bash
syntax-check clean on both scripts.

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved platform version stamping to keep the version record
correctly labeled and consistent during upgrades.
* Updated tenant Kubernetes resources to move from v1.30 to v1.31 as
part of the migration flow.

* **Chores**
* Standardized migration behavior by using a shared version-stamping
helper for more reliable updates.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
pull Bot pushed a commit to medampudi/cozystack that referenced this pull request Jun 26, 2026
Migrations 45 and 46 (added by cozystack#2931) stamp the cozystack-version ConfigMap
with an inline `kubectl apply` heredoc instead of sourcing
lib/cozystack-version.sh and calling stamp_cozystack_version, the way 42/43/44
do. The cozystack-version-stamp.bats guard forbids any migration >= 42 from
bypassing the helper, so `make bats-unit-tests` fails on migration 45.

This only surfaces in PR CI (make unit-tests); main-push CI does not run the
bats suite, so cozystack#2931 landed red on this test and now blocks every PR that
merges current main.

Route both stamps through the helper. The helper renders a byte-identical
labeled manifest (golden-pinned in the bats suite), so this is behaviour-
preserving — it just restores the no-label-drift guarantee the heredoc form
could silently lose.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/kubernetes Issues or PRs related to the tenant Kubernetes app 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.

4 participants