Repository navigation
[Epic] Adopt the kstatus readiness model platform-wide (Flux 2.8 / helm-controller v1.5.0) #2642
Description
Activity
Status update (as of main @ f0f1431)
Base PR #2602 (Flux v2.7.3 → v2.8.0) is merged. Per-package migration status below.
✅ Task 1 — Unblock the Flux 2.8 bump (kubernetes package) — DONE
Resolved via Option B (child-side, remove the guard) in
f6c737983
(fix(kubernetes): drop lookup-guarded parent-HR dependsOn on tenant addon child HRs).- No kubernetes child HR carries
dependsOn: [{Release.Name}]against the parent anymore. - Remaining
dependsOnentries point to sibling children (e.g.cilium → -gateway-api-crds). - The
lookup/Release.Nameguard is gone;ouroboros.yamlretains only an explanatory
comment on why no render-time guard is wired.
Option A (operator-side knob) was not taken.
⚠️ Task 2 — Class 2 umbrellas adopthealthCheckExprs— ~1 of 12-
packages/extra/seaweedfs— hashealthCheckExprs+waitStrategy: poller
(targets its backing CNPGCluster; landed incidentally via29c6afc84) -
packages/apps/openbao -
packages/apps/qdrant -
packages/apps/bucket -
packages/apps/nats -
packages/apps/harbor -
packages/apps/tenant(seaweedfs, ingress, etcd, monitoring, info) -
packages/extra/external-dns -
packages/extra/ingress -
packages/extra/monitoring -
packages/system/bootbox -
packages/system/cozystack-basics
❌ Task 3 — Class 1 leaves, operator support — NOT STARTED
-
ComponentInstall/Packageinapi/v1alpha1/— nohealthCheckExprs/waitStrategy/disableWaitfields. -
buildHelmReleaseSpecininternal/operator/package_reconciler.go— no readiness knobs threaded. - No candidate leaf (postgres, kafka, mongodb, mariadb, clickhouse, redis, rabbitmq,
foundationdb, opensearch, valkey, vm-instance, vm-disk) adopted anything.
✅ Task 4 — Out of scope guard respected
--feature-gates=UseHelm3Defaults=trueis not enabled anywhere (only appears in CRD
description text in the embeddedfluxcd.yaml).Remaining work: Task 2 (11/12 umbrellas) and all of Task 3 (operator plumbing + every leaf).
- No kubernetes child HR carries
- addedkind/cleanupCategorizes issue or PR as related to cleanup of code, process, or technical debtCategorizes issue or PR as related to cleanup of code, process, or technical debtarea/platformIssues or PRs related to platform infrastructure (bundle, flux, talos, installer)Issues or PRs related to platform infrastructure (bundle, flux, talos, installer)epicA large development increment that brings definite value to Cozystack usersA large development increment that brings definite value to Cozystack users
on Jun 4, 2026 - changed the title
[-]Flux 2.8 readiness model migration: per-package work[/-][+][Epic] Adopt the kstatus readiness model platform-wide (Flux 2.8 / helm-controller v1.5.0)[/+]on Jun 8, 2026 - addedkind/api-changeCategorizes issue or PR as related to adding, removing, or otherwise changing an APICategorizes issue or PR as related to adding, removing, or otherwise changing an APIand removedkind/cleanupCategorizes issue or PR as related to cleanup of code, process, or technical debtCategorizes issue or PR as related to cleanup of code, process, or technical debt
on Jun 8, 2026 - addedtriage/acceptedIndicates an issue is ready to be actively worked onIndicates an issue is ready to be actively worked onpriority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to completeImportant over the long term, but may not be staffed and/or may need multiple releases to complete
on Jul 1, 2026 myasnikovdaniil commented
on Aug 14, 2026 ContributorAuthorMore actionsRe-audited this epic against
origin/mainat09dc7f47fand rewrote the body to match the tree. Six of the 25 checkboxes are now checked: Task 1 Option B landed in #2602 (f6c737983),extra/seaweedfsin #2601 (29c6afc84),extra/monitoringin #3215 (f9afc7309), and both Task 3 plumbing items in #3273, which also bumpedhelm-controller/apito v1.5.1 and threaded the apiserver HR builder the epic never named. The typed API is a live but unconsumed surface: noApplicationDefinitionunderpackages/system/*-rd/cozyrds/and noPackageSourceunderpackages/core/platform/sources/setswaitStrategyorhealthCheckExprs, so every Task 3 leaf is still at zero. Task 2 needed a correction. Per the readiness audit in #3264, 8 of the 12 umbrellas wrap only native workloads or child HRs that kstatus already assesses, so an expression there is a no-op, and the rows are annotated rather than deleted because that audit lives in an unmerged draft. #3264 itself is blocked rather than idle: CHANGES_REQUESTED since 2026-07-20 overfailedpredicates that never fire, one of which is already merged inseaweedfs-db.yaml:34and now has its own issue, while the unmergedEtcdClusterone stays in #3264's scope. A new Task 5 records the follow-ups the original body predates, including thatpackages/apps/kubernetesandkubernetes-nodesare structurally excluded from the model while they carryrelease.cozystack.io/helm-install-disable-wait. #3263 is closed as implemented by #3273, and thevalkeyleaf is dropped from Task 3 because novalkey-rdpackage exists on main.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Status as of 2026-08-13
The plumbing is finished and adoption has barely begun. Task 1 landed in #2602 via Option B, and the typed API plus its threading through both HR builders landed in #3273, but no package on
origin/mainsetswaitStrategyorhealthCheckExprsthrough that typed API, so it is a live and entirely unconsumed surface. Only two HelmRelease templates carry a kstatus gate anywhere in the tree,packages/extra/seaweedfs/templates/seaweedfs-db.yamlandpackages/extra/monitoring/templates/helmrelease.yaml, and both got there through unrelated PRs rather than through this epic. Phase 1 of the template work (#3264) is a stalled draft: CHANGES_REQUESTED since 2026-07-20 over deadfailedpredicates, with five unticked dev-cluster verification boxes and an E2E run that never reached the changed code. Two corrections to the original plan are recorded in Task 2 and Task 5 below. The "addhealthCheckExprsto all 12 umbrellas" framing is wrong for umbrellas that wrap only native workloads, andpackages/apps/kubernetespluspackages/apps/kubernetes-nodesare structurally excluded from the model as long as they carryrelease.cozystack.io/helm-install-disable-wait.Context
PR #2602 (
feat(flux): upgrade to v2.8.0 — tenant chart v0.48.0 + management-cluster embed, web UI opt-in) upgrades Flux from v2.7.3 → v2.8.0 across both planes:flux-operator/flux-instancecharts v0.33.0 → v0.48.0 (now at 0.50.0, still trackingdistribution.version: 2.8.xinpackages/system/fluxcd/values.yaml).internal/fluxinstall/manifests/fluxcd.yamlregenerated from theflux-aiotimoni bundle (helm-controller:v1.5.0,internal/fluxinstall/manifests/fluxcd.yaml:8097).In particular
helm-controllergoes v1.4.3 → v1.5.0. Beyond the version bump, v1.5.0 flips two defaults to align with Helm v4 (verbatim from the v1.5.0 CHANGELOG):waitStrategy.namenow defaults topoller(kstatus polling of every applied resource, including managed HelmReleases), and can be reverted tolegacy(Helm v3 wait, core kinds only) via theUseHelm3Defaultscontroller feature gate. Cozystack does not enable that gate.The kstatus default makes
HelmRelease.Readymean "every applied resource is healthy" instead of "helm install returned". That is correct for the end-to-end semantics we want between packages,spec.dependsOnbecomes a real readiness gate instead of an ordering hint, but it interacts badly with existing umbrella patterns and is currently unreachable for leaf packages.Symptoms
After applying #2602 on dev10,
tenant-root/kubernetes-testHR entersReconcilingand its child HRs reportDependencyNotReady. Cycle shape: parent waits on each child via kstatus → each child hasdependsOn: [parent]→ deadlock. Same shape (different chains) already fixed in60aecd5f(vpavpa-for-vpanested HR) and unrelated yaml-merge bug ina23b5d27(seaweedfsdependsOncollision). Retrospective write-up indocs/release.md:565-571.Tasks
1. Unblock the Flux 2.8 bump (Class 3, single package), DONE
packages/apps/kubernetesis the only package in the monorepo whose child HRs carrydependsOn: [{Release.Name}](guarded by alookup). Pick one:internal/operator/package_reconciler.go::buildHelmReleaseSpecto setspec.waitStrategy.name: legacy(orspec.install.disableWait: true+spec.upgrade.disableWait: true) on the parent HR forcozystack.kubernetes-application. Not taken, superseded by Option B.{{- if lookup … Release.Name }}block from the 19 child HR templates inpackages/apps/kubernetes/templates/helmreleases/. Landed in feat(flux): upgrade to v2.8.0 + chart fixes for strict SSA & kstatus (folds #2612) #2602 (f6c737983). No child HR references the bare parent any more, all remainingdependsOnentries point at siblings, andpackages/apps/kubernetes/templates/helmreleases/ouroboros.yaml:93keeps the comment explaining why no render-time guard is wired.2. Class 2 umbrellas adopt
healthCheckExprs(template edits only)Correction (#3264 readiness audit, unmerged, needs sign-off). The original "add
healthCheckExprsto every umbrella" framing is wrong. kstatus already assesses Deployments, StatefulSets and child HelmReleases natively, and a built-in workload has no empty-status race to close, so an expression on such an umbrella is a no-op. A gate is required only where the HR's manifest is a custom resource. Rows below are annotated with that verdict, and the 8 marked no-op are candidates for closing rather than pending work.packages/apps/openbao(inner HR → OpenBao CR), no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/apps/qdrant, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/apps/bucket, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/apps/nats, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/apps/harbor, needed (CNPGCluster), drafted in feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264, blocked on a deadfailedpredicate. The bundledRedisFailoverhas no status subresource and cannot be gated.packages/apps/tenant, the package now has 7 HR children, not the 5 originally listed (computeplane,etcd,gateway,info,ingress,monitoring,seaweedfs). Needed foretcd(EtcdCluster) andgateway(TenantGateway), both drafted in feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264, no-op for the other five.packages/extra/external-dns, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/extra/ingress, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/extra/monitoring, landed in fix(monitoring): surface missing VictoriaLogs and gate dead-end vlinsert ExternalName #3215 (f9afc7309):packages/extra/monitoring/templates/helmrelease.yaml:30setswaitStrategy.name: poller,:51gates onVLCluster.status.updateStatus, field pins inpackages/extra/monitoring/tests/helmrelease_test.yaml:22-43. feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 would extend it toVMCluster/VMAgent/VMAlert/VMAlertmanager,Grafanaand the backing CNPGCluster.packages/extra/seaweedfs, landed in refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system #2601 (29c6afc84):packages/extra/seaweedfs/templates/seaweedfs-db.yaml:24-25setswaitStrategy.name: poller,:30-35gates on the CNPGCluster. The sibling-systemchild (packages/extra/seaweedfs/templates/seaweedfs.yaml:195) is intentionally ungated (native workloads). See Task 5 for the contestedfailedpredicate.packages/system/bootbox, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 auditpackages/system/cozystack-basics, no-op per feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264 audit3. Class 1 leaves, operator support for per-package knobs
These packages render the underlying CR directly, the wrapping HR is built in Go. Adopting Model B requires plumbing new fields through the operator and through the apiserver. The epic originally named only the operator, but every catalog app's HR is built by
pkg/registry/apps/application/rest.go::convertApplicationToHelmRelease.cozystack.io/v1alpha1.ComponentInstall(orPackage) inapi/v1alpha1/withhealthCheckExprs, optionallywaitStrategyanddisableWait. Landed in feat(api): typed waitStrategy/healthCheckExprs for kstatus readiness (#2642) #3273:api/v1alpha1/packagesource_types.go:154,164onComponentInstallandapi/v1alpha1/applicationdefinitions_types.go:97,111onApplicationDefinitionRelease, both typed askustomize.CustomHealthCheckand gated by apoller;legacyenum. Prerequisitego.modbump ofhelm-controller/apiv1.4.3 → v1.5.1 landed in the same PR (83f0d3bc9,go.mod:11).disableWaitwas not added, the pre-existingrelease.cozystack.io/helm-install-disable-waitannotation (pkg/config/config.go:59) already covers it.internal/operator/package_reconciler.go::buildHelmReleaseSpec. Landed in feat(api): typed waitStrategy/healthCheckExprs for kstatus readiness (#2642) #3273 atinternal/operator/package_reconciler.go:100-106, plus the apiserver builder atpkg/registry/apps/application/rest.go:1645-1646fed frompkg/cmd/server/start.go:274-275. Shared coupling helperconfig.ResolveWaitStrategy(pkg/config/config.go:220-228) defaults topollerwhenever expressions are set without an explicit strategy. Tests:internal/operator/package_reconciler_test.go:164,pkg/registry/apps/application/rest_helmrelease_spec_test.go:140,pkg/config/waitstrategy_test.go:30.ApplicationDefinitioninpackages/system/*-rd/cozyrds/and noPackageSourceinpackages/core/platform/sources/sets either field. Candidate leaves and their natural readiness signals:packages/apps/postgres→ CNPGCluster.status.phase == 'Cluster in healthy state'packages/apps/kafka→ StrimziKafkaReady conditionpackages/apps/mongodb→ PerconaPerconaServerMongoDB.status.statepackages/apps/mariadb→ mariadb-operatorMariaDBReadypackages/apps/clickhouse→ClickHouseInstallation.status.statuspackages/apps/redis,rabbitmq,foundationdb,opensearch(valkeydropped, novalkey-rdpackage exists on main)packages/apps/vm-instance,vm-disk→ KubeVirtVirtualMachine.status.printableStatus4. Out of scope (explicit)
--feature-gates=UseHelm3Defaults=trueon helm-controller. The global flip would undo every Model B gain from tasks 2 and 3 and force the project back onto Helm v3 wait semantics. Verified holding:UseHelm3Defaultsappears on main only inside CRD description text ininternal/fluxinstall/manifests/fluxcd.yaml, and the only feature gates actually set there areExternalArtifact=true(:8014,:8079).5. Follow-ups discovered after the epic was written
failedpredicate inpackages/extra/seaweedfs/templates/seaweedfs-db.yaml:34may be dead code, tracked in its own issue, Verify whether the seaweedfs-db CNPG failed health-check predicate is reachable #3807. TheEtcdClusterstatus.brokenMembers > 0predicate flagged by the same review stays inside feat(flux): kstatus health gates on CR-wrapping umbrella HelmReleases #3264's scope, since that predicate is not merged.helm-install-disable-wait.packages/apps/kubernetesandpackages/apps/kubernetes-nodescarryrelease.cozystack.io/helm-install-disable-wait: "true"(packages/system/kubernetes-rd/cozyrds/kubernetes.yaml:28,packages/system/kubernetes-nodes-rd/cozyrds/kubernetes-nodes.yaml:16) to break a real chicken-and-egg with the talos-reconcile Job. Upstream evaluateshealthCheckExprsonly when the Helm action has wait enabled, so any gate on those two kinds is a silent no-op (documented atapi/v1alpha1/applicationdefinitions_types.go:105-109). Either accept them as permanently outside the kstatus model or find a wait-compatible way to break the cycle.git grep -il kstatus -- docs/hits onlydocs/changelogs/v1.5.0.mdand the retrospective atdocs/release.md:565-571. There is no guidance on when a gate is required, how to write the three predicates, or thatdisableWaitdefeats them.healthCheckExprs/waitStrategyonApplicationDefinitionandComponentInstall. That surface and its threading landed in #3273, so anyone arriving from a link to [API proposal] Typed healthCheckExprs / waitStrategy on ApplicationDefinition and ComponentInstall (kstatus readiness, #2642) #3263 should read feat(api): typed waitStrategy/healthCheckExprs for kstatus readiness (#2642) #3273 instead.References
seaweedfs-dbreference gate): refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system #2601monitoring-systemreference gate): fix(monitoring): surface missing VictoriaLogs and gate dead-end vlinsert ExternalName #3215