Skip to content

feat(platform): add support for non-Talos Kubernetes deployments - #1939

Merged
Andrei Kvapil (kvaps) merged 27 commits into
cozystack:mainfrom
lexfrei:feat/generic-k8s-support
Feb 2, 2026
Merged

feat(platform): add support for non-Talos Kubernetes deployments#1939
Andrei Kvapil (kvaps) merged 27 commits into
cozystack:mainfrom
lexfrei:feat/generic-k8s-support

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Jan 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Enable Cozystack deployment on generic Kubernetes clusters (kubeadm, k3s, RKE2, etc.) by making Talos-specific configurations conditional and adding a new isp-full-generic bundle variant.

Problem 1: Cilium uses hardcoded Talos API endpoint

Issue: Cilium is configured to connect to Kubernetes API at localhost:7445 (Talos KubePrism). On non-Talos clusters, the API server runs on standard <node-ip>:6443.

Solution: Add networking.apiServer.host and networking.apiServer.port to platform values, passed to Cilium via bundle template.

Why this approach: Allows per-deployment configuration while keeping Talos defaults for backwards compatibility.

Problem 2: Cilium cgroup automount disabled

Issue: values-talos.yaml sets cgroup.autoMount.enabled: false because Talos mounts cgroups. On Ubuntu/Debian, Cilium needs to mount cgroups itself.

Solution: Add networking.cilium.cgroup.autoMount configuration and create cilium-generic / kubeovn-cilium-generic networking variants without values-talos.yaml.

Why this approach: Separate variants avoid complex conditional logic in templates and make the difference explicit.

Problem 3: KubeOVN helm lookup fails on fresh clusters

Issue: KubeOVN chart uses lookup to find control-plane nodes. During initial deployment, lookup returns empty results causing installation failure.

Solution: Add networking.kubeovn.MASTER_NODES parameter to explicitly pass node IPs when needed.

Why this approach: Helm lookup is unreliable during initial deployment; explicit configuration is more predictable.

Problem 4: LinstorSatelliteConfiguration breaks non-Talos nodes

Issue: cozystack-talos LinstorSatelliteConfiguration removes DRBD init containers (drbd-module-loader, drbd-shutdown-guard). These are required on Ubuntu/Debian where DRBD modules aren't pre-loaded.

Solution: Add talos.enabled value (default: true) and wrap the configuration in {{- if .Values.talos.enabled }}.

Why this approach: Conditional rendering is cleaner than node selectors (Talos has no unique default label) and maintains backwards compatibility.

Problem 5: linstor-scheduler image tag invalid for k3s

Issue: k3s reports version as v1.35.0+k3s1. The + character is invalid in Docker image tags, causing InvalidImageName error for kube-scheduler.

Solution: Strip distribution suffix using regexReplaceAll "\\+.*$" in the helper template.

Why this approach: k3s version v1.35.0+k3s1 is based on upstream v1.35.0, so stripping the suffix produces the correct upstream image tag. This is automatic and doesn't require manual version specification.

Problem 6: No bundle for non-Talos deployments

Issue: isp-full bundle always uses Talos-specific networking and doesn't pass talos.enabled=false to linstor.

Solution: Add isp-full-generic bundle variant that uses kubeovn-cilium-generic networking and passes talos.enabled=false to linstor.

Why this approach: Dedicated bundle variant makes deployment straightforward — users just set bundle-name: isp-full-generic without needing post-install patches.

Files Changed

Path Change
packages/core/platform/values.yaml Add apiServer, cilium.cgroup, kubeovn.MASTER_NODES config
packages/core/platform/sources/networking.yaml Add generic variants
packages/core/platform/templates/bundles/system.yaml Add isp-full-generic bundle
packages/system/linstor/values.yaml Add talos.enabled
packages/system/linstor/templates/satellites-talos.yaml Conditional rendering
packages/system/linstor-scheduler/.../templates/_helpers.tpl Strip version suffix
packages/system/linstor-scheduler/.../templates/deployment.yaml Use helper for image tag

Testing

Tested on k3s v1.35.0+k3s1 with Ubuntu 24.04 LTS. All HelmReleases deploy successfully without post-install patches.

Breaking Changes

None. Existing Talos deployments continue to work unchanged with default values.


Closes #1933

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for generic (non-Talos) Kubernetes clusters with new isp-full-generic platform bundle
    • Introduced generic and hosted deployment variants for cozystack-operator
    • Added generic networking variants with Cilium and KubeOVN support
    • Expanded IAAS, PAAS, and NAAS bundle support for generic deployments
    • Improved backup retention configuration
  • Bug Fixes

    • Fixed Kubernetes version handling to work with distribution-specific suffixes
  • Chores

    • Made LINSTOR Talos satellite support optional

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

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds comprehensive support for deploying Cozystack on non-Talos Kubernetes clusters (k3s, kubeadm, RKE2) by introducing -generic variants for networking and operator deployments, making Talos-specific features conditional, adding configurable API endpoints and node selectors, and creating the isp-full-generic bundle variant.

Changes

Cohort / File(s) Summary
Operator Manifests
Makefile, packages/core/installer/templates/cozystack-operator.yaml, packages/core/installer/templates/cozystack-operator-generic.yaml, packages/core/installer/templates/cozystack-operator-hosted.yaml, hack/upload-assets.sh
Added new generic and hosted operator deployment variants; updated Makefile to render both variants and upload to release assets. Generic variant reads API endpoints from ConfigMap fallback values.
Platform Networking Sources
packages/core/platform/sources/networking.yaml
Added cilium-generic and kubeovn-cilium-generic variants without Talos-specific configurations, enabling cilium deployment on generic Kubernetes clusters.
Platform Bundle System
packages/core/platform/templates/bundles/system.yaml, packages/core/platform/values.yaml
Added isp-full-generic variant with logic to parse API endpoints from ConfigMap or chart values, configure kubeovn/cilium for generic k8s, and include cozystack engine components with control-plane node selectors.
Platform Package Source
packages/core/installer/templates/packagesource.yaml, packages/core/platform/values-isp-full-generic.yaml
Added isp-full-generic variant to PackageSource and new platform values file enabling all bundles (system, iaas, paas, naas) with migrations enabled.
Platform Helpers & Apps
packages/core/platform/templates/_helpers.tpl, packages/core/platform/templates/apps.yaml
Created new cozystack.platform.system.common-packages template consolidating shared system packages; updated apps.yaml to read root-host override from cozystack ConfigMap.
Bundle Variant Gating
packages/core/platform/templates/bundles/iaas.yaml, packages/core/platform/templates/bundles/naas.yaml, packages/core/platform/templates/bundles/paas.yaml
Expanded variant conditions to include isp-full-generic alongside existing variants in iaas, naas, and paas bundle gating logic.
Linstor Talos Support
packages/system/linstor/values.yaml, packages/system/linstor/templates/satellites-talos.yaml
Added talos.enabled flag (default true) to conditionally render Talos-specific LinstorSatelliteConfiguration resources; updated template with conditional wrapping.
Linstor Scheduler
packages/system/linstor-scheduler/charts/linstor-scheduler/templates/_helpers.tpl, packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml
Modified kubeVersion helper to strip distribution suffixes from Kubernetes version; updated deployment to use new helper for image tag resolution.
Cozystack API & Lineage Controller
packages/system/cozystack-api/templates/deployment.yaml, packages/system/cozystack-api/values.yaml, packages/system/lineage-controller-webhook/templates/daemonset.yaml, packages/system/lineage-controller-webhook/values.yaml
Added configurable nodeSelector blocks with platform-specific guidance (Talos vs. generic k8s); moved from hardcoded selectors to value-driven configuration.
Velero Configuration
packages/system/velero/charts/velero/values.yaml, packages/system/velero/values.yaml
Updated kubectl image to alpine/k8s:1.35.0; added velero.upgradeCRDs: false with comments explaining CRD management and kubectl compatibility.
Operator Manifest Base
packages/core/installer/templates/cozystack-operator.yaml
Updated KUBERNETES_SERVICE_HOST to quoted string value; added comment for Talos KubePrism endpoint clarity.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • cozystack/cozystack#1484: Modifies Velero chart and value files (kubectl image updates, velero configuration) with similar code-level changes to kubectl and velero components.

Suggested reviewers

  • lllamnyp

Poem

🐰 Hops with glee, the rabbit sings,
Generic clusters now take wing!
No more Talos chains so tight,
k3s and kubeadm shine so bright!
Variants bloom like garden flowers,
Cozystack's reach now holds new powers!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main change: adding support for non-Talos Kubernetes deployments. It is specific, concise, and directly reflects the primary objective of the changeset.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding objectives from issue #1933: making Cilium/networking configurable (via new apiServer host/port and generic variants), conditionalizing Talos-specific configurations (Linstor satellites, KubeOVN MASTER_NODES), handling Kubernetes version suffixes (linstor-scheduler), and providing ready-to-use isp-full-generic bundle variant.
Out of Scope Changes check ✅ Passed All changes are directly related to supporting non-Talos Kubernetes deployments. Modifications to operator manifests, Cilium/KubeOVN networking variants, Linstor conditional Talos logic, version suffix handling, and new bundle variants are all within scope of the linked issue.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello Aleksei Sviridkin (@lexfrei), 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 significantly expands Cozystack's compatibility by enabling its deployment on generic Kubernetes clusters, such as kubeadm, k3s, and RKE2. It addresses several Talos-specific configurations that previously hindered broader adoption, making them conditional or providing generic alternatives. The changes introduce new configuration options and a dedicated bundle variant to streamline the installation process on diverse Kubernetes environments, ensuring a more flexible and robust platform.

Highlights

  • Cilium API Endpoint Configuration: Cilium's Kubernetes API endpoint is now configurable, allowing it to connect to the standard <node-ip>:6443 on non-Talos clusters instead of the Talos-specific localhost:7445.
  • Cilium Cgroup Automount: Added configuration for Cilium's cgroup automount, enabling it to mount cgroups itself on non-Talos systems like Ubuntu/Debian, where Talos doesn't pre-mount them. New cilium-generic and kubeovn-cilium-generic networking variants were introduced.
  • KubeOVN Master Node Lookup Reliability: KubeOVN deployments can now explicitly specify MASTER_NODES to avoid helm lookup failures on fresh clusters, improving reliability during initial setup.
  • LinstorSatelliteConfiguration for Non-Talos Nodes: The LinstorSatelliteConfiguration that removes DRBD init containers is now conditionally applied based on a talos.enabled flag, ensuring DRBD modules are correctly loaded on generic Linux distributions.
  • Linstor-Scheduler Image Tag for k3s: A helper template was added to strip distribution suffixes (e.g., +k3s1) from Kubernetes version strings, resolving InvalidImageName errors for the linstor-scheduler on k3s clusters.
  • New Generic Deployment Bundle: A new isp-full-generic bundle variant has been introduced to simplify deployments on non-Talos Kubernetes clusters, automatically configuring generic networking and Linstor settings without requiring post-install patches.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

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

Invoking Gemini

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

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

Customization

To customize 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

@lexfrei
Aleksei Sviridkin (lexfrei) marked this pull request as ready for review January 30, 2026 10:17
@dosubot dosubot Bot added size/L This PR changes 100-499 lines, ignoring generated files kind/feature Categorizes issue or PR as related to a new feature labels Jan 30, 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

The pull request effectively addresses several challenges related to deploying Cozystack on non-Talos Kubernetes clusters. The changes introduce necessary conditional configurations and a new bundle variant (isp-full-generic) to support generic Kubernetes environments. The solutions for handling Cilium's API endpoint, cgroup automount, KubeOVN's master node lookup, LinstorSatelliteConfiguration, and k3s image tag issues are well-reasoned and implemented. The approach of making Talos-specific configurations conditional and providing a dedicated generic bundle variant enhances flexibility and maintainability.

Comment on lines +43 to +58
{{- $kubeovnIpv4 := dict
"POD_CIDR" .Values.networking.podCIDR
"POD_GATEWAY" .Values.networking.podGateway
"SVC_CIDR" .Values.networking.serviceCIDR
"JOIN_CIDR" .Values.networking.joinCIDR -}}
{{- $kubeovnDict := dict "ipv4" $kubeovnIpv4 -}}
{{- if .Values.networking.kubeovn.MASTER_NODES -}}
{{- $_ := set $kubeovnDict "MASTER_NODES" .Values.networking.kubeovn.MASTER_NODES -}}
{{- end -}}
{{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}}
{{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}}
{{- $ciliumValues := dict "cilium" (dict
"k8sServiceHost" .Values.networking.apiServer.host
"k8sServicePort" .Values.networking.apiServer.port
"cgroup" (dict "autoMount" (dict "enabled" .Values.networking.cilium.cgroup.autoMount))) -}}
{{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for constructing $kubeovnIpv4, $kubeovnDict, $kubeovnValues, and $ciliumValues is duplicated between the isp-full and isp-full-generic bundle variants. Consider extracting this common logic into a helper template to improve maintainability and reduce redundancy. This would make the template cleaner and easier to update if the networking configuration logic changes in the future.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 4b5b08a - extracted common packages into cozystack.platform.system.common-packages helper template.

@lexfrei
Aleksei Sviridkin (lexfrei) marked this pull request as draft January 30, 2026 10:54
Comment on lines +7 to +12
iaas:
enabled: false
paas:
enabled: false
naas:
enabled: false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If it is isp-full probably these bundles should be enabled by default

Suggested change
iaas:
enabled: false
paas:
enabled: false
naas:
enabled: false
iaas:
enabled: true
paas:
enabled: true
naas:
enabled: true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in current PR - values-isp-full-generic.yaml already has iaas/paas/naas enabled by default.

Comment thread packages/core/platform/values.yaml Outdated
Comment on lines +35 to +39
# Cilium cgroup configuration
# Set autoMount to true for non-Talos clusters
cilium:
cgroup:
autoMount: false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This could be hidden, I bet it works just as a charm in many distros by default

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will remove this option from user-facing values. For isp-full-generic it's already hardcoded to true in template.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 87b3408 - removed cilium.cgroup from user-facing values, hardcoded in template.

Comment thread packages/core/platform/values.yaml Outdated
Comment on lines +40 to +43
# KubeOVN configuration for non-Talos clusters
# MASTER_NODES: comma-separated list of master node IPs (empty = use helm lookup)
kubeovn:
MASTER_NODES: ""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This option is nice to have, please add comment, what exactly helm looks up for

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will add comment explaining that KubeOVN helm chart uses lookup to find nodes with node-role.kubernetes.io/control-plane label. On fresh clusters lookup returns empty, so this allows explicit override.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 87b3408 - added detailed comment explaining helm lookup behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In overall this file contains higher level user options, we should metoin only mandatory ones.
Experienced users are always have the oportunity to overwrite default vaules directly in kind: Package

Plese consider if we can hide implementation specifics from here. Eg. instead of specifying cilium and kubeovn maps, using something common, eg

networking:
  podGateway: "10.244.0.1"
  serviceCIDR: "10.96.0.0/16"
  joinCIDR: "100.64.0.0/16"

are common for any CNI managed by bundle

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree. Will simplify by removing CNI-specific options from user-facing values. The generic options (podCIDR, podGateway, serviceCIDR, joinCIDR) are already there. Implementation details can be derived in templates.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 87b3408 - removed apiServer and cilium config from user-facing values. CNI-specific values are now hardcoded in bundle templates.

Comment on lines -65 to 70
Get the kubernetes version we should assume for creating scheduler configs
Get the kubernetes version we should assume for creating scheduler configs.
Strips distribution suffixes like +k3s1, +rke2r1 from version string.
*/}}
{{- define "linstor-scheduler.kubeVersion" }}
{{- .Values.scheduler.image.compatibleKubernetesRelease | default .Capabilities.KubeVersion.Version }}
{{- $version := .Values.scheduler.image.compatibleKubernetesRelease | default .Capabilities.KubeVersion.Version }}
{{- regexReplaceAll "\\+.*$" $version "" }}
{{- end }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please send this patch to upstream project
https://github.com/piraeusdatastore/linstor-scheduler-extender

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will create upstream PR to piraeusdatastore/linstor-scheduler-extender with the kubeVersion helper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Upstream PR created: piraeusdatastore/helm-charts#77

Comment on lines -65 to +69
Get the kubernetes version we should assume for creating scheduler configs
Get the kubernetes version we should assume for creating scheduler configs.
Strips distribution suffixes like +k3s1, +rke2r1 from version string.
*/}}
{{- define "linstor-scheduler.kubeVersion" }}
{{- .Values.scheduler.image.compatibleKubernetesRelease | default .Capabilities.KubeVersion.Version }}
{{- $version := .Values.scheduler.image.compatibleKubernetesRelease | default .Capabilities.KubeVersion.Version }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please send this patch to upstream project
https://github.com/piraeusdatastore/linstor-scheduler-extender

Comment on lines 24 to 25
- name: drbd-module-loader
$patch: delete

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is open for discussion. On one hand, enabling kernel module loading via the Piraeus operator will simplify the user installation flow.
On the other hand, it might be less stable than having the kernel module precompiled and installed as part of the Linux distribution.

Overall, I’m fine with this approach, but let’s revisit and re-evaluate it after we gain some production experience.

Comment on lines 65 to 73
{{- end }}
env:
- name: KUBERNETES_SERVICE_HOST
value: localhost
value: {{ .Values.cozystackOperator.kubernetesServiceHost | quote }}
- name: KUBERNETES_SERVICE_PORT
value: "7445"
value: {{ .Values.cozystackOperator.kubernetesServicePort | quote }}
hostNetwork: true
tolerations:
- key: "node.kubernetes.io/not-ready"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is example manifest, it is not intended to be deployed using Helm, this chart is used only for generating example manifests for releases page.

Here we have two options:

  • generate separate manifest for the non-talos clusters and enforce user to specify KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT in it
  • Find a way to specify these options in separate manifest, eg. this can be done by valueFrom: { confgiMap: {name: ""}}

The point is, the standard flow for installing Cozystack now is:

kubectl apply -f https://github.com/cozystack/cozystack/releases/latest/download/cozystack-crds.yaml
kubectl apply -f https://github.com/cozystack/cozystack/releases/latest/download/cozystack-operator.yaml

wget https://raw.githubusercontent.com/cozystack/cozystack/refs/heads/main/packages/core/installer/example/platform.yaml
vim platform.yaml
kubectl apply -f platform.yaml

for updating:

kubectl apply -f https://github.com/cozystack/cozystack/releases/latest/download/cozystack-crds.yaml
kubectl apply -f https://github.com/cozystack/cozystack/releases/latest/download/cozystack-operator.yaml

So it would be nice to have the oportunity to not enforce user change original manifests with no necessity.

option 1:

apiVersion: v1
kind: ConfigMap
metadata:
  name: cozystack-operator-env-defaults
data:
  KUBERNETES_SERVICE_HOST: "localhost"
  KUBERNETES_SERVICE_PORT: "7445"
env:
  - name: KUBERNETES_SERVICE_HOST
    valueFrom:
      configMapKeyRef:
        name: cozystack-operator-env-overrides
        key: KUBERNETES_SERVICE_HOST
        optional: true

  - name: KUBERNETES_SERVICE_PORT
    valueFrom:
      configMapKeyRef:
        name: cozystack-operator-env-overrides
        key: KUBERNETES_SERVICE_PORT
        optional: true

User can create cozystack-operator-env-overrides and overwrite the defaults (not sure if it works)

option 2:

separate two installation files for three diferent cases:

for talos:

env:
- name: KUBERNETES_SERVICE_HOST
  value: localhost
- name: KUBERNETES_SERVICE_PORT
  value: "7445"

for non-talos:

env:
  - name: KUBERNETES_SERVICE_HOST
    valueFrom:
      configMapKeyRef:
        name: cozystack-operator-config
        key: KUBERNETES_SERVICE_HOST
        optional: false

  - name: KUBERNETES_SERVICE_PORT
    valueFrom:
      configMapKeyRef:
        name: cozystack-operator-config
        key: KUBERNETES_SERVICE_PORT
        optional: false

this won't start operator, until user manualy create cozystack-operator-config configmap

for hosted version:

env: []

Personally I like the second option most

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will implement option 2 - separate manifest files for talos/generic/hosted deployment targets. This keeps the installation flow clean without requiring users to modify original manifests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 5091f4d - implemented option 2: separate manifest files for talos/generic/hosted deployment targets.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome 👌

@kvaps Andrei Kvapil (kvaps) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hey thanks for contribution, just put few thing regarding this

@lexfrei
Aleksei Sviridkin (lexfrei) marked this pull request as ready for review January 30, 2026 18:24

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

🤖 Fix all issues with AI agents
In `@packages/core/installer/templates/cozystack-operator-hosted.yaml`:
- Around line 6-27: The manifest grants overly broad privileges: the
ServiceAccount "cozystack" is bound to ClusterRoleBinding "cozystack" with
cluster-admin and the namespace has pod-security.kubernetes.io/enforce:
privileged; instead, create a narrowly scoped ClusterRole listing only the
required API groups/resources/verbs for the operator and change the
ClusterRoleBinding "cozystack" to reference that new role, or make the role name
configurable; remove or relax the pod-security.kubernetes.io/enforce: privileged
label on the namespace (or make the PSS exemption configurable) and document the
minimal PSS requirements so hosted installs don’t automatically receive
privileged exemptions.

In `@packages/core/platform/templates/bundles/system.yaml`:
- Around line 40-55: The template may call urlParse on a scheme-less
apiServerEndpoint which makes $parsed.host empty; before calling urlParse,
detect if apiServerEndpoint lacks a scheme and prepend "https://" (or the
appropriate default) so urlParse returns a proper host; then continue extracting
$hostPort and set $apiHost/$apiPort as before (preserve the fallback "6443");
update any logic that sets $apiServerEndpoint, $parsed, $apiHost and $apiPort to
use the normalized URL; keep using splitList ":" but be aware of future IPv6
bracketed addresses if/when supported.
- Around line 81-83: MASTER_NODES_LABEL is currently hardcoded to
"node-role.kubernetes.io/control-plane=true", which fails for kubeadm where the
control-plane label has an empty value; make MASTER_NODES_LABEL configurable via
Helm values (e.g., add a value like kubeOvn.masterNodeLabel or
kube-ovn.masterNodeLabel) and change the template to use that value (fallback to
either "node-role.kubernetes.io/control-plane" or
"node-role.kubernetes.io/control-plane=true") so DaemonSet nodeSelector
expressions that reference the kube-ovn dict use the configurable label; update
values.yaml to document options (empty value vs "=true") and ensure templates
that set nodeSelector for kube-ovn DaemonSets use the new MASTER_NODES_LABEL
variable.
🧹 Nitpick comments (3)
packages/system/velero/charts/velero/values.yaml (1)

339-346: Avoid directly editing vendored upstream chart values.

Modifying kubectl.image.repository and kubectl.image.tag directly in the vendored chart (charts/velero/values.yaml) will be overwritten during chart re-vendoring and creates maintenance burden.

Override these values in the parent packages/system/velero/values.yaml instead:

velero:
  kubectl:
    image:
      repository: alpine/k8s
      tag: "1.35.0"

As per coding guidelines: "Do NOT directly edit upstream Helm charts in vendored charts directory; use proper chart vendoring mechanisms."

packages/core/installer/templates/cozystack-operator-generic.yaml (1)

1-90: Consider adding nodeSelector for control-plane scheduling.

The Talos variant relies on hostNetwork: true to access localhost:7445 (KubePrism), which works because any node can reach its local API proxy. For the generic variant, if KUBERNETES_SERVICE_HOST points to a specific control-plane IP, scheduling to worker nodes could cause connectivity issues depending on network topology.

Consider whether this deployment should include a nodeSelector to schedule on control-plane nodes, or document that the ConfigMap should contain a universally reachable API server address (e.g., load balancer VIP).

packages/core/installer/templates/cozystack-operator-hosted.yaml (1)

68-77: Consider dnsPolicy: ClusterFirstWithHostNet when using hostNetwork.

With hostNetwork: true, pods inherit host DNS settings; if the operator relies on Kubernetes service DNS, resolution can fail. Add an explicit dnsPolicy or document the reliance on host DNS.

➕ Add ClusterFirstWithHostNet
       hostNetwork: true
+      dnsPolicy: ClusterFirstWithHostNet

Comment on lines +6 to +27
labels:
cozystack.io/system: "true"
pod-security.kubernetes.io/enforce: privileged
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: cozystack
namespace: cozy-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack
subjects:
- kind: ServiceAccount
name: cozystack
namespace: cozy-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

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:

# First, let's see the complete file to understand the full context
cat -n packages/core/installer/templates/cozystack-operator-hosted.yaml

Repository: cozystack/cozystack

Length of output: 2611


🏁 Script executed:

# Check if there's any documentation about why cluster-admin is needed
fd -t f "README" packages/core/ | head -20

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

# Search for comments or documentation about this operator
rg -i "cozystack.operator|hosted|privilege" packages/core/installer/ -A 3

Repository: cozystack/cozystack

Length of output: 10870


🏁 Script executed:

# Search for any custom ClusterRole definitions for cozystack operator
rg "kind: ClusterRole" packages/core/installer/ -A 30

Repository: cozystack/cozystack

Length of output: 9824


🏁 Script executed:

# Look for any documentation or README about operator requirements
find packages/core/installer -name "*.md" -o -name "*.txt" | xargs cat 2>/dev/null | head -200

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

# Search for any comments or explanations about why cluster-admin is needed
rg -i "cluster.admin|RBAC|permission|scope" packages/core/installer/ --type yaml

Repository: cozystack/cozystack

Length of output: 1098


🏁 Script executed:

# Check if there are any issues or discussions about RBAC scoping
git log --oneline --all -- packages/core/installer/templates/cozystack-operator-hosted.yaml | head -10

Repository: cozystack/cozystack

Length of output: 124


Scope down default privileges for hosted installs.

The namespace is enforced as privileged and the ServiceAccount is bound to cluster-admin, which grants full cluster rights to any pod in cozy-system. If only the operator needs elevated privileges, consider scoping RBAC to required resources and narrowing PSS exemptions, or make this configurable.

🤖 Prompt for AI Agents
In `@packages/core/installer/templates/cozystack-operator-hosted.yaml` around
lines 6 - 27, The manifest grants overly broad privileges: the ServiceAccount
"cozystack" is bound to ClusterRoleBinding "cozystack" with cluster-admin and
the namespace has pod-security.kubernetes.io/enforce: privileged; instead,
create a narrowly scoped ClusterRole listing only the required API
groups/resources/verbs for the operator and change the ClusterRoleBinding
"cozystack" to reference that new role, or make the role name configurable;
remove or relax the pod-security.kubernetes.io/enforce: privileged label on the
namespace (or make the PSS exemption configurable) and document the minimal PSS
requirements so hosted installs don’t automatically receive privileged
exemptions.

Comment thread packages/core/platform/templates/bundles/system.yaml
Comment thread packages/core/platform/templates/bundles/system.yaml
@dosubot dosubot Bot added size/XXL This PR changes 1000+ lines, ignoring generated files and removed size/L This PR changes 100-499 lines, ignoring generated files labels Jan 30, 2026

@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

Caution

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

⚠️ Outside diff range comments (2)
packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml (1)

46-93: ⚠️ Potential issue | 🟠 Major

Potential upgrade break: backupClassName is now required for Plans.

Existing Plan CRs created before this change will lack the field and can fail validation on spec updates or re-apply. Ensure there is a migration/defaulting path (e.g., conversion webhook or upgrade job) or keep the field optional for a deprecation window.

internal/controller/dashboard/static_refactored.go (1)

1896-1949: ⚠️ Potential issue | 🟠 Major

Service spec.type field selector is unsupported and will cause the API call to fail.

Line 1911-1913: Kubernetes Services do not support .spec.type as a field selector (only metadata.name and metadata.namespace are supported). This will cause the list call to fail with an invalid selector error, leaving the table empty. Use a label selector instead (add a standardized label to LoadBalancer services) or implement client-side filtering.

🤖 Fix all issues with AI agents
In `@cmd/backup-controller/main.go`:
- Around line 178-182: The manager registers the BackupJob webhook via
SetupBackupJobWebhookWithManager (for resource BackupJob) but there are no
Kubernetes webhook manifests, so add a ValidatingWebhookConfiguration (or
Mutating if needed) and a ClusterIP Service and associated certificate/TLS
secret plus CABundle wiring so the API server can reach and trust the webhook;
create templates for the ValidatingWebhookConfiguration that references the
service name and namespace and includes the caBundle field populated from the
signing CA secret, add a Service and Secret for the webhook server certs, and
ensure the controller bootstrap creates/refreshes the cert secret (or uses
cert-manager) so the webhook (registered by SetupBackupJobWebhookWithManager) is
actually invoked by the API server.

In `@hack/migrate-to-version-1.0.sh`:
- Around line 62-67: The current EXTERNAL_IPS parsing can produce empty YAML
list items when expose-external-ips contains trailing commas or whitespace;
update the transformation that builds EXTERNAL_IPS (the block that reads
COZYSTACK_CM and sets EXTERNAL_IPS) to split on commas, trim whitespace from
each token, and filter out empty tokens before prefixing with "          - " and
joining; ensure the code that handles the empty-case still sets
EXTERNAL_IPS="[]" when no valid IPs remain.

In `@internal/backupcontroller/factory/backupjob_test.go`:
- Around line 142-149: The test's prefix assertion in the validate function
(inside backupjob_test.go) only checks length and can miss incorrect prefixes;
replace the length-based check with a real prefix check using
strings.HasPrefix(job.Name, "test-plan") (importing the strings package if not
already) and update the error message to reflect that the BackupJob name should
start with the plan name.

In `@packages/apps/mongodb/templates/mongodb.yaml`:
- Around line 24-27: The pmm and backup images are hardcoded in the Helm
template; add image override keys to values.yaml (pmm.image and backup.image)
with the current pinned defaults and update the template references in
packages/apps/mongodb/templates/mongodb.yaml to use those values (e.g.,
reference .Values.pmm.image and .Values.backup.image, providing the existing
pinned strings as defaults) so deployments can override images for
air-gapped/private registries while keeping the pinned defaults.

In `@packages/core/platform/images/migrations/Dockerfile`:
- Line 3: The Dockerfile currently fetches install.sh from refs/heads/main and
pipes it to sh; change this to download the script from the v1.6.1 release tag
(use the release tarball or raw file URL for tag v1.6.1 instead of
refs/heads/main), download the corresponding checksums file for v1.6.1, verify
the script’s integrity (e.g., sha256sum) before executing, and avoid direct
pipe-to-sh by saving the script to disk and executing it only after
verification; update the RUN invocation that currently contains "wget -O-
https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s
-- -v 1.6.1" to implement these steps.

In `@packages/core/platform/sources/cozy-proxy.yaml`:
- Line 21: The change to the Helm chart releaseName (the releaseName key
currently set to "cozy-proxy") will create a new release and orphan the previous
one during upgrades; update the PR to preserve the original releaseName or add
explicit migration/cleanup instructions: either revert releaseName to the
previous value, or add upgrade notes and a migration script that detects the old
release (by its old releaseName), performs resource adoption or deletes the old
release safely, and documents the manual steps; reference the releaseName key
and the cozy-proxy release in the notes and ensure any CI/CD Helm upgrade jobs
are updated to target the correct release name.

In
`@packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml`:
- Around line 32-34: The vendored chart template was edited directly (the
container named "kube-scheduler" and its image expression using
.Values.scheduler.image.repository and .Values.scheduler.image.tag / include
"linstor-scheduler.kubeVersion"), which must be reverted; instead, set the
desired image/tag via the wrapper chart's values (override
.Values.scheduler.image.repository and .Values.scheduler.image.tag in the
top-level values.yaml) or apply a kustomize/helmfile overlay/patch that updates
that image field—do not modify the vendored deployment.yaml template directly.
🧹 Nitpick comments (7)
hack/e2e-prepare-cluster.bats (1)

139-160: Consider removing or documenting the commented-out mirror endpoints.

Large blocks of commented-out configuration can become stale and confusing over time. If these alternative nexus mirrors are environment-specific options, consider either:

  1. Removing them entirely if no longer needed.
  2. Moving them to a separate example/documentation file if they're useful for specific deployment scenarios.
api/backups/v1alpha1/plan_types.go (1)

68-74: Consider validating BackupClassName is non-empty.

This avoids resolving a blank class name at runtime.

Suggested validation
 	// BackupClassName references a BackupClass that contains strategy and storage configuration.
 	// The BackupClass will be resolved to determine the appropriate strategy and storage
 	// based on the ApplicationRef.
+	// +kubebuilder:validation:MinLength=1
 	BackupClassName string `json:"backupClassName"`
internal/controller/dashboard/factory.go (1)

177-218: Deduplicate the Resource Quotas block.

The Info/Tenant branches are identical; consider merging them to reduce repetition.

♻️ Optional refactor
-	if kind == "Info" {
+	if kind == "Info" || kind == "Tenant" {
 		rightColStack = append(rightColStack,
 			antdFlexVertical("resource-quotas-block", 4, []any{
 				antdText("resource-quotas-label", true, "Resource Quotas", map[string]any{
 					"fontSize":     float64(20),
 					"marginBottom": float64(12),
 				}),
 				map[string]any{
 					"type": "EnrichedTable",
 					"data": map[string]any{
 						"id":                   "resource-quotas-table",
 						"baseprefix":           "/openapi-ui",
 						"clusterNamePartOfUrl": "{2}",
 						"customizationId":      "factory-resource-quotas",
 						"fetchUrl":             "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/resourcequotas",
 						"pathToItems":          []any{`items`},
 					},
 				},
 			}),
 		)
 	}
-	if kind == "Tenant" {
-		rightColStack = append(rightColStack,
-			antdFlexVertical("resource-quotas-block", 4, []any{
-				antdText("resource-quotas-label", true, "Resource Quotas", map[string]any{
-					"fontSize":     float64(20),
-					"marginBottom": float64(12),
-				}),
-				map[string]any{
-					"type": "EnrichedTable",
-					"data": map[string]any{
-						"id":                   "resource-quotas-table",
-						"baseprefix":           "/openapi-ui",
-						"clusterNamePartOfUrl": "{2}",
-						"customizationId":      "factory-resource-quotas",
-						"fetchUrl":             "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/resourcequotas",
-						"pathToItems":          []any{`items`},
-					},
-				},
-			}),
-		)
-	}
.github/workflows/pull-requests.yaml (2)

263-293: Consider quoting shell variables and using find for robustness.

Static analysis flags potential issues with unquoted variables and the ls | xargs pattern.

🔧 Proposed shell script improvements
-          for app in $(ls hack/e2e-apps/*.bats | xargs -n1 basename | cut -d. -f1); do
+          for app in $(find hack/e2e-apps -maxdepth 1 -name '*.bats' -exec basename {} .bats \;); do
             echo "::group::Testing $app"
             attempt=0
             success=false
             until [ $attempt -ge 3 ]; do
-              if make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-$app; then
+              if make -C packages/core/testing SANDBOX_NAME="$SANDBOX_NAME" test-apps-"$app"; then
                 success=true
                 break
               fi

295-329: Consider quoting SANDBOX_NAME in cleanup steps.

The SANDBOX_NAME variable should be quoted to prevent word splitting issues if it ever contains spaces.

🔧 Proposed fix
       - name: Collect report
         if: always()
         run: |
-          cd /tmp/$SANDBOX_NAME
-          make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-report || true
+          cd /tmp/"$SANDBOX_NAME"
+          make -C packages/core/testing SANDBOX_NAME="$SANDBOX_NAME" collect-report || true

Apply similar quoting to other steps using $SANDBOX_NAME.

internal/backupcontroller/backupclass_resolver.go (1)

58-69: Avoid returning a shared Parameters map.
strategy.Parameters is returned directly; downstream mutation would modify the cached object. Consider copying the map before returning.

🔧 Suggested defensive copy
-		if strategyAPIGroup == appAPIGroup && strategy.Application.Kind == applicationRef.Kind {
-			return &ResolvedBackupConfig{
-				StrategyRef: strategy.StrategyRef,
-				Parameters:  strategy.Parameters,
-			}, nil
-		}
+		if strategyAPIGroup == appAPIGroup && strategy.Application.Kind == applicationRef.Kind {
+			params := map[string]string{}
+			for k, v := range strategy.Parameters {
+				params[k] = v
+			}
+			return &ResolvedBackupConfig{
+				StrategyRef: strategy.StrategyRef,
+				Parameters:  params,
+			}, nil
+		}
internal/backupcontroller/velerostrategy_controller.go (1)

61-61: Consider adding nil guard for resolved parameter.

While the calling code should guarantee resolved is non-nil after successful BackupClass resolution, a defensive nil check would prevent unexpected panics if the calling contract changes.

🛡️ Optional defensive check
 func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1alpha1.BackupJob, resolved *ResolvedBackupConfig) (ctrl.Result, error) {
 	logger := getLogger(ctx)
+	if resolved == nil {
+		return r.markBackupJobFailed(ctx, j, "resolved backup config is nil")
+	}
 	logger.Debug("reconciling Velero strategy", "backupjob", j.Name, "phase", j.Status.Phase)

Comment on lines +178 to +182
// Register BackupJob webhook for validation (immutability of backupClassName)
if err = backupsv1alpha1.SetupBackupJobWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "BackupJob")
os.Exit(1)
}

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for BackupJob in webhook configurations
echo "=== Searching for BackupJob in YAML configs ==="
rg -n "BackupJob" config -g '*.yaml'
echo ""
echo "=== Searching for backupjob in YAML configs ==="
rg -n "backupjob" config -g '*.yaml'
echo ""
echo "=== Listing config directory structure ==="
find config -type f -name '*.yaml' | head -20

Repository: cozystack/cozystack

Length of output: 356


🏁 Script executed:

#!/bin/bash
# Explore repository structure to find config/manifest locations
echo "=== Repository root structure ==="
ls -la | head -20
echo ""
echo "=== Find all directories at root level ==="
find . -maxdepth 1 -type d | sort
echo ""
echo "=== Search for webhook-related YAML files ==="
fd -e yaml -e yml | rg -i webhook
echo ""
echo "=== Search for BackupJob references in YAML files ==="
fd -e yaml -e yml -x rg -l "BackupJob" {}

Repository: cozystack/cozystack

Length of output: 9726


🏁 Script executed:

#!/bin/bash
# Explore backup-controller package structure
echo "=== Backup-controller directory structure ==="
find ./packages/system/backup-controller -type f | sort
echo ""
echo "=== Search for webhook configurations in backup-controller ==="
find ./packages/system/backup-controller -name '*webhook*' -o -name '*Webhook*'
echo ""
echo "=== Check for ValidatingWebhookConfiguration or MutatingWebhookConfiguration ==="
rg -n "ValidatingWebhookConfiguration|MutatingWebhookConfiguration" ./packages/system/backup-controller
echo ""
echo "=== Search for BackupJob in backup-controller ==="
rg -n "BackupJob" ./packages/system/backup-controller

Repository: cozystack/cozystack

Length of output: 2501


🏁 Script executed:

#!/bin/bash
# Check deployment template for webhook configuration
echo "=== deployment.yaml template ==="
cat ./packages/system/backup-controller/templates/deployment.yaml
echo ""
echo "=== values.yaml ==="
cat ./packages/system/backup-controller/values.yaml

Repository: cozystack/cozystack

Length of output: 2133


Add ValidatingWebhookConfiguration and service definitions for BackupJob webhook.

The BackupJob webhook is registered in the manager but webhook configuration manifests are missing from packages/system/backup-controller/templates/. Add ValidatingWebhookConfiguration and/or MutatingWebhookConfiguration resources along with a webhook service, certificate configuration, and CABundle wiring. Without these, the webhook will not be invoked by the API server.

🤖 Prompt for AI Agents
In `@cmd/backup-controller/main.go` around lines 178 - 182, The manager registers
the BackupJob webhook via SetupBackupJobWebhookWithManager (for resource
BackupJob) but there are no Kubernetes webhook manifests, so add a
ValidatingWebhookConfiguration (or Mutating if needed) and a ClusterIP Service
and associated certificate/TLS secret plus CABundle wiring so the API server can
reach and trust the webhook; create templates for the
ValidatingWebhookConfiguration that references the service name and namespace
and includes the caBundle field populated from the signing CA secret, add a
Service and Secret for the webhook server certs, and ensure the controller
bootstrap creates/refreshes the cert secret (or uses cert-manager) so the
webhook (registered by SetupBackupJobWebhookWithManager) is actually invoked by
the API server.

Comment on lines +62 to +67
EXTERNAL_IPS=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-external-ips"] // ""')
if [ -z "$EXTERNAL_IPS" ]; then
EXTERNAL_IPS="[]"
else
EXTERNAL_IPS=$(echo "$EXTERNAL_IPS" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}')
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.

⚠️ Potential issue | 🟡 Minor

Trim and filter external IP entries to avoid empty YAML list items.

Trailing commas or whitespace in expose-external-ips will emit empty list items (or invalid IP strings) in the generated YAML. Trim and skip empty tokens before formatting.

Suggested fix
-EXTERNAL_IPS=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-external-ips"] // ""')
-if [ -z "$EXTERNAL_IPS" ]; then
-    EXTERNAL_IPS="[]"
-else
-    EXTERNAL_IPS=$(echo "$EXTERNAL_IPS" | sed 's/,/\n/g' | awk 'BEGIN{print}{print "          - "$0}')
-fi
+EXTERNAL_IPS_RAW=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-external-ips"] // ""')
+if [ -z "$EXTERNAL_IPS_RAW" ]; then
+    EXTERNAL_IPS="[]"
+else
+    EXTERNAL_IPS=$(echo "$EXTERNAL_IPS_RAW" | tr ',' '\n' | awk '
+      { gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0) }
+      length($0) > 0 { print "          - " $0 }
+    ')
+    if [ -z "$EXTERNAL_IPS" ]; then
+        EXTERNAL_IPS="[]"
+    fi
+fi
📝 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
EXTERNAL_IPS=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-external-ips"] // ""')
if [ -z "$EXTERNAL_IPS" ]; then
EXTERNAL_IPS="[]"
else
EXTERNAL_IPS=$(echo "$EXTERNAL_IPS" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}')
fi
EXTERNAL_IPS_RAW=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-external-ips"] // ""')
if [ -z "$EXTERNAL_IPS_RAW" ]; then
EXTERNAL_IPS="[]"
else
EXTERNAL_IPS=$(echo "$EXTERNAL_IPS_RAW" | tr ',' '\n' | awk '
{ gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0) }
length($0) > 0 { print " - " $0 }
')
if [ -z "$EXTERNAL_IPS" ]; then
EXTERNAL_IPS="[]"
fi
fi
🤖 Prompt for AI Agents
In `@hack/migrate-to-version-1.0.sh` around lines 62 - 67, The current
EXTERNAL_IPS parsing can produce empty YAML list items when expose-external-ips
contains trailing commas or whitespace; update the transformation that builds
EXTERNAL_IPS (the block that reads COZYSTACK_CM and sets EXTERNAL_IPS) to split
on commas, trim whitespace from each token, and filter out empty tokens before
prefixing with "          - " and joining; ensure the code that handles the
empty-case still sets EXTERNAL_IPS="[]" when no valid IPs remain.

Comment on lines +142 to +149
validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) {
if job.Name == "" {
t.Error("BackupJob name should be generated")
}
// Name should start with plan name
if len(job.Name) < len("test-plan") {
t.Errorf("BackupJob name = %v, should start with test-plan", job.Name)
}

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

Prefix check doesn’t validate the actual prefix.

The current length check can pass even if the name doesn’t start with the plan. Prefer strings.HasPrefix.

🔧 Suggested fix
 import (
 	"testing"
 	"time"
+	"strings"
 
 	backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
@@
-				if len(job.Name) < len("test-plan") {
-					t.Errorf("BackupJob name = %v, should start with test-plan", job.Name)
-				}
+				if !strings.HasPrefix(job.Name, "test-plan-") {
+					t.Errorf("BackupJob name = %v, should start with test-plan-", job.Name)
+				}
📝 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
validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) {
if job.Name == "" {
t.Error("BackupJob name should be generated")
}
// Name should start with plan name
if len(job.Name) < len("test-plan") {
t.Errorf("BackupJob name = %v, should start with test-plan", job.Name)
}
import (
"strings"
"testing"
"time"
backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ... test code ...
validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) {
if job.Name == "" {
t.Error("BackupJob name should be generated")
}
// Name should start with plan name
if !strings.HasPrefix(job.Name, "test-plan-") {
t.Errorf("BackupJob name = %v, should start with test-plan-", job.Name)
}
🤖 Prompt for AI Agents
In `@internal/backupcontroller/factory/backupjob_test.go` around lines 142 - 149,
The test's prefix assertion in the validate function (inside backupjob_test.go)
only checks length and can miss incorrect prefixes; replace the length-based
check with a real prefix check using strings.HasPrefix(job.Name, "test-plan")
(importing the strings package if not already) and update the error message to
reflect that the BackupJob name should start with the plan name.

Comment on lines 24 to 27
pmm:
enabled: false
image: {{ .Values.images.pmm }}
image: percona/pmm-client:2.44.1
serverHost: ""

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:

# First, let's examine the MongoDB template file
fd -t f "mongodb.yaml" packages/apps/mongodb/

Repository: cozystack/cozystack

Length of output: 107


🏁 Script executed:

# Read the file to confirm the hardcoded images at lines 24-27 and 124-127
cat -n packages/apps/mongodb/templates/mongodb.yaml | sed -n '20,30p;120,130p'

Repository: cozystack/cozystack

Length of output: 683


🏁 Script executed:

# Check if there's a values.yaml in the same directory or parent
fd -t f "values.yaml" packages/apps/mongodb/

Repository: cozystack/cozystack

Length of output: 96


🏁 Script executed:

# Search for .Values.images references in the template to see if override pattern is already used
rg "\.Values\.images" packages/apps/mongodb/templates/

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

# Check the structure of the mongodb chart to understand the chart type
ls -la packages/apps/mongodb/

Repository: cozystack/cozystack

Length of output: 889


🏁 Script executed:

# Read the values.yaml file to see if image overrides are defined
cat packages/apps/mongodb/values.yaml

Repository: cozystack/cozystack

Length of output: 4307


🏁 Script executed:

# Check entire template for .Values usage to see if override pattern exists elsewhere
rg "\.Values\." packages/apps/mongodb/templates/mongodb.yaml | head -20

Repository: cozystack/cozystack

Length of output: 1351


🏁 Script executed:

# Check values.schema.json to see if images are supposed to be configurable
cat packages/apps/mongodb/values.schema.json

Repository: cozystack/cozystack

Length of output: 8275


Add image override configuration for PMM and backup.

Hardcoding percona/pmm-client:2.44.1 and percona/percona-backup-mongodb:2.11.0 breaks deployments in air-gapped or private-registry environments. Define image fields in values.yaml and use template references to allow overrides while maintaining pinned defaults.

Suggested changes

Add to values.yaml:

pmm:
  image: percona/pmm-client:2.44.1

Add to backup in values.yaml:

backup:
  image: percona/percona-backup-mongodb:2.11.0
  ...

Update template at lines 26 and 126:

-    image: percona/pmm-client:2.44.1
+    image: {{ .Values.pmm.image }}
...
-    image: percona/percona-backup-mongodb:2.11.0
+    image: {{ .Values.backup.image }}
📝 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
pmm:
enabled: false
image: {{ .Values.images.pmm }}
image: percona/pmm-client:2.44.1
serverHost: ""
pmm:
enabled: false
image: {{ .Values.pmm.image }}
serverHost: ""
🤖 Prompt for AI Agents
In `@packages/apps/mongodb/templates/mongodb.yaml` around lines 24 - 27, The pmm
and backup images are hardcoded in the Helm template; add image override keys to
values.yaml (pmm.image and backup.image) with the current pinned defaults and
update the template references in packages/apps/mongodb/templates/mongodb.yaml
to use those values (e.g., reference .Values.pmm.image and .Values.backup.image,
providing the existing pinned strings as defaults) so deployments can override
images for air-gapped/private registries while keeping the pinned defaults.

FROM alpine:3.22

RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.5.0
RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.6.1

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:

cozyhr GitHub install.sh version pinning tag v1.6.1

💡 Result:

To pin cozyhr’s install.sh to tag v1.6.1 (instead of tracking main), change the ref in the URL from refs/heads/main to refs/tags/v1.6.1:

curl -sSL https://github.com/cozystack/cozyhr/raw/refs/tags/v1.6.1/hack/install.sh | sh -s

The repo README shows the same installer pattern for main (unpinned). [1]
v1.6.1 is an existing tag/release in cozystack/cozyhr (dated Jan 27, 2026). [2]

Sources: [1] (github.com) [2] (github.com)

Citations:


🏁 Script executed:

# Check if cozyhr repository and its install script structure can be accessed
curl -s https://api.github.com/repos/cozystack/cozyhr/tags | head -20

Repository: cozystack/cozystack

Length of output: 981


🏁 Script executed:

# Verify if v1.6.1 tag exists in cozyhr repository
curl -s https://api.github.com/repos/cozystack/cozyhr/releases | grep -A 2 "v1.6.1"

Repository: cozystack/cozystack

Length of output: 1938


Pin install.sh to a release tag instead of tracking main.

Fetching install.sh from refs/heads/main is mutable and increases supply-chain risk. The version flag -v 1.6.1 indicates intent to install a specific version, so pin the script source to the corresponding release tag. The v1.6.1 release exists and includes a checksums file for integrity verification.

Suggested fix (pin to tag and avoid pipe-to-sh)
-RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.6.1
+RUN wget -O /tmp/install.sh https://github.com/cozystack/cozyhr/raw/refs/tags/v1.6.1/hack/install.sh \
+ && sh /tmp/install.sh -v 1.6.1 \
+ && rm /tmp/install.sh
🤖 Prompt for AI Agents
In `@packages/core/platform/images/migrations/Dockerfile` at line 3, The
Dockerfile currently fetches install.sh from refs/heads/main and pipes it to sh;
change this to download the script from the v1.6.1 release tag (use the release
tarball or raw file URL for tag v1.6.1 instead of refs/heads/main), download the
corresponding checksums file for v1.6.1, verify the script’s integrity (e.g.,
sha256sum) before executing, and avoid direct pipe-to-sh by saving the script to
disk and executing it only after verification; update the RUN invocation that
currently contains "wget -O-
https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s
-- -v 1.6.1" to implement these steps.

install:
namespace: cozy-system
releaseName: cozystack
releaseName: cozy-proxy

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

Release name change can break in-place upgrades.

Changing releaseName from the prior value will create a new Helm release and leave the old one orphaned on upgrade, risking duplicate resources or downtime. Please add a migration/cleanup path (or upgrade notes) to handle existing installs.

🤖 Prompt for AI Agents
In `@packages/core/platform/sources/cozy-proxy.yaml` at line 21, The change to the
Helm chart releaseName (the releaseName key currently set to "cozy-proxy") will
create a new release and orphan the previous one during upgrades; update the PR
to preserve the original releaseName or add explicit migration/cleanup
instructions: either revert releaseName to the previous value, or add upgrade
notes and a migration script that detects the old release (by its old
releaseName), performs resource adoption or deletes the old release safely, and
documents the manual steps; reference the releaseName key and the cozy-proxy
release in the notes and ensure any CI/CD Helm upgrade jobs are updated to
target the correct release name.

Comment on lines 32 to 34
- name: kube-scheduler
image: "{{ .Values.scheduler.image.repository }}:{{ .Values.scheduler.image.tag | default .Capabilities.KubeVersion.Version }}"
image: "{{ .Values.scheduler.image.repository }}:{{ .Values.scheduler.image.tag | default (include "linstor-scheduler.kubeVersion" .) }}"
securityContext:

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

# Check the linstor-scheduler directory structure
echo "=== Directory structure ==="
find packages/system/linstor-scheduler -type f -name "*.yaml" -o -name "*.yml" | head -20

# Check for Chart.yaml in the chart
echo -e "\n=== Chart.yaml content ==="
cat packages/system/linstor-scheduler/charts/linstor-scheduler/Chart.yaml 2>/dev/null || echo "No Chart.yaml found"

# Look for Chart.lock or dependencies
echo -e "\n=== Looking for vendoring indicators ==="
ls -la packages/system/linstor-scheduler/charts/linstor-scheduler/ 2>/dev/null | grep -E "(Chart\.lock|requirements|dependencies)"

# Check parent directory structure
echo -e "\n=== Parent linstor-scheduler package structure ==="
ls -la packages/system/linstor-scheduler/ 2>/dev/null

# Check if there are any references to upstream sources
echo -e "\n=== Checking for upstream indicators ==="
grep -r "source" packages/system/linstor-scheduler/charts/linstor-scheduler/Chart.yaml 2>/dev/null || echo "No upstream source found in Chart.yaml"

Repository: cozystack/cozystack

Length of output: 2613


🏁 Script executed:

#!/bin/bash

# Check how other packages in system manage charts
echo "=== Checking other system packages for comparison ==="
ls packages/system/*/charts/ 2>/dev/null | head -5

# Look for any vendoring documentation or scripts
echo -e "\n=== Looking for vendoring mechanisms ==="
find packages/system -maxdepth 2 -name "Makefile" -o -name "*.sh" | head -10 | xargs grep -l "chart\|vendor" 2>/dev/null || echo "No vendoring scripts found"

# Check if there's a Chart.lock or similar
echo -e "\n=== Checking for Chart dependencies ==="
cat packages/system/linstor-scheduler/charts/linstor-scheduler/Chart.yaml | grep -A 5 "dependencies"

Repository: cozystack/cozystack

Length of output: 395


🏁 Script executed:

#!/bin/bash

# Check the complete Chart.yaml to understand if this is a custom or vendored chart
echo "=== Full Chart.yaml for linstor-scheduler chart ==="
cat packages/system/linstor-scheduler/charts/linstor-scheduler/Chart.yaml

Repository: cozystack/cozystack

Length of output: 833


Remove direct edits from vendored upstream chart; use overlay mechanism instead.

The linstor-scheduler/charts/linstor-scheduler chart is vendored from piraeusdatastore/helm-charts. Direct modifications to its templates make upstream syncs brittle and break the clean separation between vendored dependencies and cozystack customizations.

Apply this image tag change through the wrapper chart's values.yaml (at packages/system/linstor-scheduler/values.yaml) or use a proper overlay/patch mechanism instead of editing the vendored template directly.

🤖 Prompt for AI Agents
In
`@packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml`
around lines 32 - 34, The vendored chart template was edited directly (the
container named "kube-scheduler" and its image expression using
.Values.scheduler.image.repository and .Values.scheduler.image.tag / include
"linstor-scheduler.kubeVersion"), which must be reverted; instead, set the
desired image/tag via the wrapper chart's values (override
.Values.scheduler.image.repository and .Values.scheduler.image.tag in the
top-level values.yaml) or apply a kustomize/helmfile overlay/patch that updates
that image field—do not modify the vendored deployment.yaml template directly.

…on-Talos deployments

Add networking.apiServer.host and networking.apiServer.port to platform values
to allow overriding the default Talos KubePrism settings (localhost:7445).

Also add networking.cilium.cgroup.autoMount for non-Talos clusters that need
Cilium to mount cgroups automatically.

Changes:
- packages/core/platform/values.yaml: Add apiServer and cilium.cgroup settings
- packages/core/platform/templates/bundles/system.yaml: Pass cilium values
- packages/core/installer/values.yaml: Add kubernetesServiceHost/Port
- packages/core/installer/templates/cozystack-operator.yaml: Template env vars

Closes: #1933

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
…sters

- Add networking.kubeovn.MASTER_NODES to platform values.yaml
- Conditionally include MASTER_NODES in kubeovn values when set
- Enables KubeOVN deployment on k3s/kubeadm without control-plane node labels

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Add talos.enabled value (default: true) to control whether
LinstorSatelliteConfiguration cozystack-talos is created.

This allows deploying linstor on non-Talos clusters (Ubuntu, Debian, etc.)
where DRBD init containers are required.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
…sion

k3s and RKE2 include distribution suffixes in version string
(e.g., v1.35.0+k3s1) which are not valid container image tags.

Strip everything after '+' using regexReplaceAll to produce clean
version tags like v1.35.0.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Add cilium-generic and kubeovn-cilium-generic variants that exclude
values-talos.yaml. These variants are suitable for kubeadm, k3s, RKE2
and other non-Talos Kubernetes distributions.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Add isp-full-generic bundle variant for deploying Cozystack on non-Talos
Kubernetes clusters (kubeadm, k3s, RKE2, etc.).

This variant:
- Uses kubeovn-cilium-generic networking (no values-talos.yaml)
- Passes talos.enabled=false to linstor (keeps DRBD init containers)
- Supports all standard networking configuration via platform values

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Remove trailing dash from {{- end -}} constructs in _helpers.tpl to
preserve trailing newlines after each rendered package YAML document.

Without this fix, consecutive packages are rendered without proper
separators:

  variant: default---
  apiVersion: cozystack.io/v1alpha1

This causes YAML parse errors: "mapping key 'apiVersion' already
defined" because the YAML parser sees a single document with duplicate
keys instead of two separate documents.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
For isp-full-generic bundle variant, the template now looks up the
cozystack ConfigMap to read cluster-specific settings instead of only
relying on chart default values.

This fixes the issue where Cilium was trying to connect to localhost:7445
(Talos KubePrism default) instead of the actual API server endpoint.

Values read from ConfigMap:
- api-server-endpoint: parsed to extract host/port for Cilium
- ipv4-pod-cidr, ipv4-pod-gateway, ipv4-svc-cidr, ipv4-join-cidr: for KubeOVN

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
…eric

k3s and kubeadm set node-role.kubernetes.io/control-plane=true,
while Talos uses an empty value. KubeOVN node selector needs
the exact label value to match.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
On Talos, control-plane nodes have label node-role.kubernetes.io/control-plane
with empty value. On generic k8s (k3s, kubeadm), the same label has value "true".

The lineage-controller-webhook DaemonSet was hardcoded to use empty value,
causing 0 pods scheduled on k3s clusters.

Changes:
- Add nodeSelector to lineage-controller-webhook values (default: empty for Talos)
- Update DaemonSet template to use configurable nodeSelector
- Pass nodeSelector with value "true" for isp-full-generic bundle variant

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Same issue as lineage-controller-webhook: DaemonSet uses hardcoded
nodeSelector with empty value, but k3s/kubeadm use value "true".

Changes:
- Add nodeSelector to cozystack-api values (default: empty for Talos)
- Update deployment template to use configurable nodeSelector
- Pass nodeSelector for both cozystack-api and lineage-controller-webhook
  in isp-full-generic bundle variant

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
The cozystack-values secret was using the default publishing.host value
from values.yaml (example.org) instead of reading from the cozystack
ConfigMap where the actual root-host is configured.

Add ConfigMap lookup to apps.yaml to read root-host from cozystack
ConfigMap, falling back to chart values if not present.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
…variant

- Allow iaas, paas, naas bundles when using isp-full-generic variant
- Enable all bundles by default in values-isp-full-generic.yaml
- Update bundle templates to accept isp-full-generic alongside isp-full

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Add velero to enabledPackages list for isp-full-generic to enable
backup functionality by default.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
The enabledPackages list must use fully qualified package names
(e.g., cozystack.velero) to match the helper template check.
Also add backupstrategy-controller which is required by backup-controller.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Bitnami images are forbidden and bitnamilegacy/kubectl:1.35 doesn't exist.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
rancher/kubectl uses 'v' prefix for tags (v1.35.0 vs 1.35).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
rancher/kubectl is a minimal image without shell.
alpine/k8s includes kubectl and shell for running upgrade jobs.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
The CRD upgrade pre-install job has image compatibility issues.
CRDs are installed as part of the Helm chart install anyway.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
The Velero CRD requires spec.template.spec field.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
… config

- Remove apiServer and cilium.cgroup from user-facing values.yaml
- Hardcode Talos-specific values (localhost:7445, cgroup autoMount: false)
  directly in isp-full bundle template
- isp-full-generic already hardcodes its own values
- Improve MASTER_NODES comment explaining helm lookup behavior

This hides implementation details from users as requested in review.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
…riants

Per review feedback, create separate manifest files for different
deployment targets instead of using templated values:

- cozystack-operator.yaml (Talos): hardcoded localhost:7445 (KubePrism)
- cozystack-operator-generic.yaml: reads from cozystack-operator-config
  ConfigMap (user must create before applying)
- cozystack-operator-hosted.yaml: no env override (uses in-cluster SA)

This keeps installation flow clean - users apply the manifest matching
their deployment target without needing to modify original files.

Build system updated to generate all three variants.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Extract common system packages shared between isp-full and isp-full-generic
into a helper template `cozystack.platform.system.common-packages`.

Packages moved to helper:
- kubeovn-webhook, kubeovn-plunger, cozy-proxy
- multus, metallb, reloader
- linstor-scheduler, snapshot-controller

Packages NOT in helper (differ between variants):
- networking (variant differs: kubeovn-cilium vs kubeovn-cilium-generic)
- linstor (talos.enabled differs)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Removed reference to .Values.networking.apiServer which was removed
from values.yaml. Use empty defaults and let ConfigMap or
apiServerEndpoint parsing override them.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@dosubot dosubot Bot added size/L This PR changes 100-499 lines, ignoring generated files and removed size/XXL This PR changes 1000+ lines, ignoring generated files labels Jan 30, 2026

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

🤖 Fix all issues with AI agents
In `@packages/system/linstor/templates/satellites-talos.yaml`:
- Line 1: The conditional currently uses {{- if .Values.talos.enabled }} which
evaluates false when the new key is absent (e.g., on --reuse-values); change the
condition to use Helm's default function so Talos behavior is preserved unless
explicitly disabled, e.g., replace the test with {{- if default true
.Values.talos.enabled }} (locate the conditional in the satellites-talos.yaml
template where .Values.talos.enabled is referenced).
- Line 1: The Helm template directive "{{- if .Values.talos.enabled }}" in
satellites-talos.yaml triggers yamllint parse errors; fix by either (A) adding a
YAML lint suppression to the template file (e.g., add a top-of-file yamllint
directive to disable linting for this file) or (B) update CI/lint config to
exclude Helm template files or run a Helm-aware linter for files under
packages/system/linstor/templates; locate the file named satellites-talos.yaml
and apply one of these two options so the Helm directive is not parsed by strict
yamllint.

In `@packages/system/velero/charts/velero/values.yaml`:
- Around line 339-346: The kubectl image override (kubectl.image.repository and
kubectl.image.tag) was edited inside a vendored upstream chart; remove this
direct edit from the vendored chart and instead place the image override in the
umbrella chart's values.yaml (or update the chart vendoring properly).
Specifically, delete the kubectl.image.* entries you added in the vendored chart
and add an equivalent override under the umbrella/top-level values (or re-vendor
the chart with your change) so upstream chart updates remain clean.
🧹 Nitpick comments (2)
packages/system/backup-controller/templates/strategy.yaml (1)

5-10: Parameterize TTL and namespace scope for flexibility.

Hard-coding ttl and includedNamespaces makes retention and scope changes require chart edits. Consider wiring these to values with sane defaults.

♻️ Proposed template changes
 spec:
   template:
     spec:
-      ttl: 720h
-      includedNamespaces:
-        - "*"
+      ttl: {{ .Values.backupController.veleroStrategy.ttl | default "720h" | quote }}
+      includedNamespaces:
+{{- toYaml (default (list "*") .Values.backupController.veleroStrategy.includedNamespaces) | nindent 8 }}
packages/core/installer/templates/cozystack-operator-generic.yaml (1)

50-65: Consider adding resource requests/limits for the container.

The container definition lacks resource requests and limits. While this may work in development, it's recommended to define these for production stability and cluster resource management.

💡 Example resource specification
       - name: cozystack-operator
         image: "{{ .Values.cozystackOperator.image }}"
+        resources:
+          requests:
+            cpu: 100m
+            memory: 128Mi
+          limits:
+            cpu: 500m
+            memory: 512Mi
         args:

@@ -1,3 +1,4 @@
{{- if .Values.talos.enabled }}

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

Preserve Talos default when talos.enabled is unset.
If users upgrade with --reuse-values, the new key can be absent and this condition evaluates to false, skipping Talos-specific patches. Use a default to keep prior behavior unless explicitly disabled.

💡 Suggested fix
-{{- if .Values.talos.enabled }}
+{{- if (default true .Values.talos.enabled) }}
📝 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
{{- if .Values.talos.enabled }}
{{- if (default true .Values.talos.enabled) }}
🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 1-1: syntax error: expected the node content, but found '-'

(syntax)

🤖 Prompt for AI Agents
In `@packages/system/linstor/templates/satellites-talos.yaml` at line 1, The
conditional currently uses {{- if .Values.talos.enabled }} which evaluates false
when the new key is absent (e.g., on --reuse-values); change the condition to
use Helm's default function so Talos behavior is preserved unless explicitly
disabled, e.g., replace the test with {{- if default true .Values.talos.enabled
}} (locate the conditional in the satellites-talos.yaml template where
.Values.talos.enabled is referenced).

⚠️ Potential issue | 🟠 Major

YAMLlint parse error on Helm directive.
Static analysis reports a syntax error at the first line. If CI runs yamllint on templates, it will fail on {{- ... }}. Consider excluding Helm templates from yamllint or switching to a Helm-aware linter for template files.

🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 1-1: syntax error: expected the node content, but found '-'

(syntax)

🤖 Prompt for AI Agents
In `@packages/system/linstor/templates/satellites-talos.yaml` at line 1, The Helm
template directive "{{- if .Values.talos.enabled }}" in satellites-talos.yaml
triggers yamllint parse errors; fix by either (A) adding a YAML lint suppression
to the template file (e.g., add a top-of-file yamllint directive to disable
linting for this file) or (B) update CI/lint config to exclude Helm template
files or run a Helm-aware linter for files under
packages/system/linstor/templates; locate the file named satellites-talos.yaml
and apply one of these two options so the Helm directive is not parsed by strict
yamllint.

Comment on lines 339 to +346
kubectl:
image:
repository: docker.io/bitnamilegacy/kubectl
repository: alpine/k8s
# Digest value example: sha256:d238835e151cec91c6a811fe3a89a66d3231d9f64d09e5f3c49552672d271f38.
# If used, it will take precedence over the kubectl.image.tag.
# digest:
# kubectl image tag. If used, it will take precedence over the cluster Kubernetes version.
# tag: 1.16.15
tag: "1.35.0"

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

Avoid direct edits inside vendored chart values.

This file lives under a vendored upstream chart path. Please move the kubectl image override into the umbrella values (or re-vendor the chart properly) to keep upstream updates clean.

As per coding guidelines: Do NOT directly edit upstream Helm charts in vendored charts directory; use proper chart vendoring mechanisms.

🤖 Prompt for AI Agents
In `@packages/system/velero/charts/velero/values.yaml` around lines 339 - 346, The
kubectl image override (kubectl.image.repository and kubectl.image.tag) was
edited inside a vendored upstream chart; remove this direct edit from the
vendored chart and instead place the image override in the umbrella chart's
values.yaml (or update the chart vendoring properly). Specifically, delete the
kubectl.image.* entries you added in the vendored chart and add an equivalent
override under the umbrella/top-level values (or re-vendor the chart with your
change) so upstream chart updates remain clean.

roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The RBAC configuration (cluster-admin ClusterRoleBinding and privileged PSS) is copied from the original cozystack-operator.yaml template. This PR only splits the manifest into three variants (talos/generic/hosted) without changing the RBAC model.

Scoping down privileges would be a separate improvement applicable to all three variants and is outside the scope of this PR.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Feb 2, 2026
@kvaps
Andrei Kvapil (kvaps) merged commit 4287d17 into cozystack:main Feb 2, 2026
10 checks passed
Andrei Kvapil (kvaps) added a commit to cozystack/website that referenced this pull request Feb 18, 2026
## Summary

- Add documentation for deploying Cozystack on non-Talos Kubernetes
distributions (k3s, kubeadm, RKE2) using the `isp-full-generic` bundle
- Align installation flow with the new Package-based configuration from
cozystack/cozystack#2010 (no manual ConfigMap creation)
- Place documentation under `/docs/v1/` to match the new versioning
structure

## Changes

- **New**: `content/en/docs/v1/install/kubernetes/generic.md` — full
deployment guide
- **Updated**: `content/en/docs/v1/install/kubernetes/_index.md` — add
Generic Kubernetes section alongside Talos methods

## Documentation Includes

- Host prerequisites (packages, kernel modules, services)
- Sysctl configuration with br_netfilter module loading
- Kubernetes distribution configs for k3s, kubeadm, and RKE2
- Step-by-step installation using operator manifest with REPLACE_ME
placeholder
- Package resource with platform configuration (networking CIDRs,
publishing host)
- Example Ansible playbooks for node preparation and deployment
- Troubleshooting section for common issues

## Related

- cozystack/cozystack#1939 (merged) — Added `isp-full-generic` bundle
- cozystack/cozystack#1950 — Tracking issue for complete non-Talos
support
- cozystack/cozystack#2010 — Variant-aware installer templates
(ConfigMap removal)

## Test Plan

- [ ] Verify Hugo builds without errors
- [ ] Review all internal links work correctly
- [ ] Test examples on k3s cluster
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature lgtm This PR has been approved by a maintainer size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(platform): support non-Talos Kubernetes deployments

2 participants