feat(platform): add support for non-Talos Kubernetes deployments - #1939
Conversation
📝 WalkthroughWalkthroughThis PR adds comprehensive support for deploying Cozystack on non-Talos Kubernetes clusters (k3s, kubeadm, RKE2) by introducing Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello 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
🧠 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 AssistThe 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
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 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
|
There was a problem hiding this comment.
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.
| {{- $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) -}} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Done in commit 4b5b08a - extracted common packages into cozystack.platform.system.common-packages helper template.
| iaas: | ||
| enabled: false | ||
| paas: | ||
| enabled: false | ||
| naas: | ||
| enabled: false |
There was a problem hiding this comment.
If it is isp-full probably these bundles should be enabled by default
| iaas: | |
| enabled: false | |
| paas: | |
| enabled: false | |
| naas: | |
| enabled: false | |
| iaas: | |
| enabled: true | |
| paas: | |
| enabled: true | |
| naas: | |
| enabled: true |
There was a problem hiding this comment.
Done in current PR - values-isp-full-generic.yaml already has iaas/paas/naas enabled by default.
| # Cilium cgroup configuration | ||
| # Set autoMount to true for non-Talos clusters | ||
| cilium: | ||
| cgroup: | ||
| autoMount: false |
There was a problem hiding this comment.
This could be hidden, I bet it works just as a charm in many distros by default
There was a problem hiding this comment.
Will remove this option from user-facing values. For isp-full-generic it's already hardcoded to true in template.
There was a problem hiding this comment.
Done in commit 87b3408 - removed cilium.cgroup from user-facing values, hardcoded in template.
| # KubeOVN configuration for non-Talos clusters | ||
| # MASTER_NODES: comma-separated list of master node IPs (empty = use helm lookup) | ||
| kubeovn: | ||
| MASTER_NODES: "" |
There was a problem hiding this comment.
This option is nice to have, please add comment, what exactly helm looks up for
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Done in commit 87b3408 - added detailed comment explaining helm lookup behavior.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Done in commit 87b3408 - removed apiServer and cilium config from user-facing values. CNI-specific values are now hardcoded in bundle templates.
| 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 }} | ||
|
|
There was a problem hiding this comment.
Please send this patch to upstream project
https://github.com/piraeusdatastore/linstor-scheduler-extender
There was a problem hiding this comment.
Will create upstream PR to piraeusdatastore/linstor-scheduler-extender with the kubeVersion helper.
There was a problem hiding this comment.
Upstream PR created: piraeusdatastore/helm-charts#77
| 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 }} |
There was a problem hiding this comment.
Please send this patch to upstream project
https://github.com/piraeusdatastore/linstor-scheduler-extender
| - name: drbd-module-loader | ||
| $patch: delete |
There was a problem hiding this comment.
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.
| {{- 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" |
There was a problem hiding this comment.
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_HOSTandKUBERNETES_SERVICE_PORTin 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: trueUser 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: falsethis won't start operator, until user manualy create cozystack-operator-config configmap
for hosted version:
env: []Personally I like the second option most
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Done in commit 5091f4d - implemented option 2: separate manifest files for talos/generic/hosted deployment targets.
Andrei Kvapil (kvaps)
left a comment
There was a problem hiding this comment.
Hey thanks for contribution, just put few thing regarding this
There was a problem hiding this comment.
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.repositoryandkubectl.image.tagdirectly 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.yamlinstead: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: trueto accesslocalhost:7445(KubePrism), which works because any node can reach its local API proxy. For the generic variant, ifKUBERNETES_SERVICE_HOSTpoints 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
nodeSelectorto 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: ConsiderdnsPolicy: ClusterFirstWithHostNetwhen using hostNetwork.With
hostNetwork: true, pods inherit host DNS settings; if the operator relies on Kubernetes service DNS, resolution can fail. Add an explicitdnsPolicyor document the reliance on host DNS.➕ Add ClusterFirstWithHostNet
hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet
| 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 |
There was a problem hiding this comment.
🧩 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.yamlRepository: 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 -20Repository: 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 3Repository: cozystack/cozystack
Length of output: 10870
🏁 Script executed:
# Search for any custom ClusterRole definitions for cozystack operator
rg "kind: ClusterRole" packages/core/installer/ -A 30Repository: 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 -200Repository: 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 yamlRepository: 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 -10Repository: 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.
There was a problem hiding this comment.
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 | 🟠 MajorPotential upgrade break:
backupClassNameis 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 | 🟠 MajorService
spec.typefield selector is unsupported and will cause the API call to fail.Line 1911-1913: Kubernetes Services do not support
.spec.typeas a field selector (onlymetadata.nameandmetadata.namespaceare 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:
- Removing them entirely if no longer needed.
- 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 validatingBackupClassNameis 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 usingfindfor robustness.Static analysis flags potential issues with unquoted variables and the
ls | xargspattern.🔧 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 quotingSANDBOX_NAMEin cleanup steps.The
SANDBOX_NAMEvariable 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 || trueApply similar quoting to other steps using
$SANDBOX_NAME.internal/backupcontroller/backupclass_resolver.go (1)
58-69: Avoid returning a shared Parameters map.
strategy.Parametersis 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 forresolvedparameter.While the calling code should guarantee
resolvedis 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)
| // 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) | ||
| } |
There was a problem hiding this comment.
🧩 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 -20Repository: 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-controllerRepository: 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.yamlRepository: 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.
| 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 |
There was a problem hiding this comment.
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.
| 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.
| 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) | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| pmm: | ||
| enabled: false | ||
| image: {{ .Values.images.pmm }} | ||
| image: percona/pmm-client:2.44.1 | ||
| serverHost: "" |
There was a problem hiding this comment.
🧩 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.yamlRepository: 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 -20Repository: 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.jsonRepository: 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.1Add 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.
| 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 |
There was a problem hiding this comment.
🧩 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 -sThe 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 -20Repository: 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 |
There was a problem hiding this comment.
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.
| - 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: |
There was a problem hiding this comment.
🧩 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.yamlRepository: 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>
There was a problem hiding this comment.
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
ttlandincludedNamespacesmakes 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 }} | |||
There was a problem hiding this comment.
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.
| {{- 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).
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.
| 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" |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
## 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
Summary
Enable Cozystack deployment on generic Kubernetes clusters (kubeadm, k3s, RKE2, etc.) by making Talos-specific configurations conditional and adding a new
isp-full-genericbundle 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.hostandnetworking.apiServer.portto 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.yamlsetscgroup.autoMount.enabled: falsebecause Talos mounts cgroups. On Ubuntu/Debian, Cilium needs to mount cgroups itself.Solution: Add
networking.cilium.cgroup.autoMountconfiguration and createcilium-generic/kubeovn-cilium-genericnetworking variants withoutvalues-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
lookupto find control-plane nodes. During initial deployment, lookup returns empty results causing installation failure.Solution: Add
networking.kubeovn.MASTER_NODESparameter 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-talosLinstorSatelliteConfiguration 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.enabledvalue (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, causingInvalidImageNameerror for kube-scheduler.Solution: Strip distribution suffix using
regexReplaceAll "\\+.*$"in the helper template.Why this approach: k3s version
v1.35.0+k3s1is based on upstreamv1.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-fullbundle always uses Talos-specific networking and doesn't passtalos.enabled=falseto linstor.Solution: Add
isp-full-genericbundle variant that useskubeovn-cilium-genericnetworking and passestalos.enabled=falseto linstor.Why this approach: Dedicated bundle variant makes deployment straightforward — users just set
bundle-name: isp-full-genericwithout needing post-install patches.Files Changed
packages/core/platform/values.yamlpackages/core/platform/sources/networking.yamlpackages/core/platform/templates/bundles/system.yamlpackages/system/linstor/values.yamlpackages/system/linstor/templates/satellites-talos.yamlpackages/system/linstor-scheduler/.../templates/_helpers.tplpackages/system/linstor-scheduler/.../templates/deployment.yamlTesting
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
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.