Files
Mattia Eleuteri 7c9ab0e28e fix(cozy-lib): round the sanitized CPU request up to a whole milli-CPU
cozy-lib.resources.sanitize divides the CPU limit by cpuAllocationRatio
and rendered the quotient as a bare float. When the ratio does not
divide the value evenly the result is finer than a milli-CPU, and the
VPA admission webhook rejects it outright:

  admission webhook "vpa.k8s.io" denied the request:
  minAllowed: CPU [62500u] must be a whole number of milli CPUs

packages/extra/etcd routes its VPA minAllowed/maxAllowed through the
helper, so 250m / 4 = 62.5m left the etcd HelmRelease looping
UpgradeFailed -> RollbackFailed forever: the rollback target renders the
same invalid value. Since v1.6.0 HelmReleases wait on each other, so
that one stuck app also blocks tenant-root, cozystack-basics and
kubevirt-cdi-operator, and without CDI the Talos worker rollover cannot
import the worker OS image. Only the divisors of 250 worked as ratios,
which is an artefact of one chart's hardcoded floor rather than a real
constraint on the operator.

The request is rounded up, never down, so it stays at or above the
intended fraction of the limit. It is rounded only when it has to be:
if the milli-CPU value equals its own ceiling, the float keeps its
current rendering, so nothing changes on the default ratio of 10 or on
any ratio that divides evenly; otherwise it renders as the ceiling in
m. The comparison is exact, with no tolerance. sprig's divf and mulf run
on shopspring/decimal rather than float64, so mulf (divf 0.25 10.0)
1000.0 renders exactly 25 and there is no binary-float noise on this
codepath to absorb. A tolerance would reintroduce the bug: a value
whole-milli only to within it, such as 100.0000004m at ratio 1, would
read as exact and render "0.1000000004", rejected for the same reason
62.5m is. The same predicate covers sub-milli input, since ceil carries
any positive request to at least 1m, and a zero request stays "0".

Fixes #2917

Assisted-by: LLM
Signed-off-by: Mattia Eleuteri <mattia@hidora.io>
2026-09-29 17:04:00 +02:00
..
2024-08-12 14:47:11 +02:00
2024-07-16 17:31:52 +02:00
2025-09-24 12:32:37 +02:00

Etcd-cluster

This chart renders an EtcdCluster (etcd-operator.cozystack.io/v1alpha2) managed by the cozystack etcd-operator, plus the cert-manager CA chain and a Kamaji DataStore. TLS uses secretRef mode: this chart's own cert-manager Certificate resources issue the server, operator-client and peer certs from the etcd-issuer / etcd-peer-issuer CA Issuers, and the EtcdCluster only references the resulting Secrets via serverSecretRef / operatorClientSecretRef / peer.secretRef (the operator itself mints nothing). This is deliberate: spec.tls is immutable in v1alpha2 and etcd-migrate adopts legacy clusters into exactly this secretRef shape, so the chart must match it or every post-adoption reconcile is rejected. etcd runs cert-only (--client-cert-auth, no password auth); consumers authenticate by presenting a commonName=root client cert (etcd-client-tls) signed by the same CA.

Backups

Backups are driven by the Cozystack BackupClass flow: a BackupJob / RestoreJob against the Etcd strategy (strategy.backups.cozystack.io/v1alpha1). See examples/backups/etcd/ for the end-to-end demo and internal/backupcontroller/etcdstrategy_controller.go for the driver. The driver materialises one EtcdSnapshot (etcd-operator.cozystack.io/v1alpha2) per BackupJob; restore is in-place (the driver suspends this chart's HelmRelease, deletes the live EtcdCluster, and re-creates it with spec.bootstrap.restore.source.s3 populated from the Backup artefact's coordinates).

The legacy backup.* values block and the EtcdBackupSchedule CRD no longer exist under the v1alpha2 operator — scheduled backups are expressed as a BackupClass / Plan instead.

Parameters

Common parameters

Name Description Type Value
version etcd version (X.Y.Z) to run, set on the EtcdCluster's spec.version; the operator pulls etcd from its own configured image. string 3.6.11
size Persistent Volume size. quantity 4Gi
storageClass StorageClass used to store the data. string ""
replicas Number of etcd replicas. int 3
resources Resource configuration for etcd. object {}
resources.cpu Number of CPU cores allocated. quantity 1000m
resources.memory Amount of memory allocated. quantity 512Mi
affinity Affinity rules set verbatim on the EtcdCluster's spec.affinity, replacing the chart default. When left empty the chart applies a soft (preferred) podAntiAffinity that spreads members across nodes by kubernetes.io/hostname on a best-effort basis, so a single node failure is less likely to take out more than one member. The default is intentionally soft: a hard rule would leave a member Pending when there are fewer schedulable nodes than replicas, or when node-local storage pins a member to a node. Set a hard requiredDuringSchedulingIgnoredDuringExecution rule here to forbid co-location outright (only on clusters with enough nodes and non-node-pinned storage). Note that the operator applies affinity only when it creates or replaces a member Pod and does not reschedule running members, so on a cluster that already has co-located members the rule takes effect only as each member is replaced, not retroactively. object {}