Skip to content

fix(capi): add startupProbe to capi-controller-manager to survive cert provisioning delay - #2946

Closed
myasnikovdaniil wants to merge 1 commit into
mainfrom
fix/capi-core-startup-probe
Closed

myasnikovdaniil wants to merge 1 commit into
mainfrom
fix/capi-core-startup-probe

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

The capi-controller-manager liveness probe has no initialDelaySeconds
(defaults to 0), giving the container only 30s (3×10s) to serve /healthz.
In controller-runtime, the health endpoint starts after the webhook server,
which needs its TLS cert from cert-manager. During a loaded install,
cert-manager issues many certs simultaneously and the CAPI webhook cert can
take >30s — causing a kill/restart loop (6+ restarts observed in E2E CI).

A startupProbe with failureThreshold: 30 gives up to 300s for initial
startup, after which the regular livenessProbe takes over. This is the
standard pattern for slow-starting containers with external dependencies.

Test plan

  • CI green — specifically no capi-controller-manager CrashLoopBackOff in E2E cozyreport

Summary by CodeRabbit

  • Chores
    • Enhanced service startup health checks to improve deployment reliability and stability during initialization.

…t provisioning delay

The capi-controller-manager liveness probe has no initialDelaySeconds
(defaults to 0), giving the container only 30s (3×10s) to serve /healthz.
In controller-runtime, the health endpoint starts after the webhook server,
which needs its TLS cert from cert-manager. During a loaded install,
cert-manager is busy issuing many certs simultaneously and the CAPI webhook
cert can take >30s — causing a kill loop (6+ restarts observed in CI).

A startupProbe with failureThreshold=30 gives up to 300s for the initial
startup, after which the regular livenessProbe takes over for ongoing
health monitoring. This is the standard pattern for slow-starting
containers with external dependencies.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a crash loop issue in the capi-controller-manager caused by slow certificate provisioning from cert-manager. By introducing a startupProbe, the controller is given sufficient time to initialize its webhook server and health endpoint before the liveness probe takes over, ensuring better stability during high-load deployments.

Highlights

  • Startup Probe Configuration: Added a startupProbe to the capi-controller-manager deployment to allow for longer initialization times during certificate provisioning.
  • Stability Improvement: Configured a failureThreshold of 30 and periodSeconds of 10 to provide up to 300 seconds for the container to become ready, preventing unnecessary restart loops.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

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

Invoking Gemini

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

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

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@github-actions github-actions Bot added the size/XS This PR changes 0-9 lines, ignoring generated files label Jun 17, 2026
@coderabbitai

coderabbitai Bot commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 68bca445-eac9-43f6-9f5a-e84235861ff4

📥 Commits

Reviewing files that changed from the base of the PR and between 8c411d8 and 4b9b1b4.

📒 Files selected for processing (1)
  • packages/system/capi-providers-core/files/core-components.yaml

📝 Walkthrough

Walkthrough

A startupProbe using HTTP GET on /healthz (port healthz) with failureThreshold: 30 and periodSeconds: 10 is added to a container in core-components.yaml for the capi-providers-core system package, inserted before the existing livenessProbe.

Changes

CAPI Providers Core – startupProbe

Layer / File(s) Summary
startupProbe addition
packages/system/capi-providers-core/files/core-components.yaml
Adds a startupProbe block (HTTP GET /healthz on port healthz, failureThreshold: 30, periodSeconds: 10) positioned immediately before the existing livenessProbe in the affected container spec.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Suggested labels

kind/bug, size/M, area/monitoring

Suggested reviewers

  • kvaps
  • lllamnyp
  • lexfrei
  • androndo
  • sircthulhu
  • IvanHunters

Poem

🐇 A startup probe hops into place,
Checking /healthz at a measured pace,
Thirty failures before a frown,
Ten seconds each — no rushing down.
The liveness probe nods, "Welcome, friend!"
Health checks complete from start to end. 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(capi): add startupProbe to capi-controller-manager to survive cert provisioning delay' directly describes the main change: adding a startupProbe to handle the cert provisioning delay issue in capi-controller-manager.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/capi-core-startup-probe

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

❤️ Share

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

@github-actions github-actions Bot added area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/bug Categorizes issue or PR as related to a bug labels Jun 17, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request adds a startupProbe to the cluster-api-controller container in core-components.yaml to ensure proper startup behavior. The review feedback highlights that the PR does not fully comply with the repository's commit and PR requirements, specifically pointing out an invalid commit scope ('capi' instead of 'cluster-api' or 'capi-providers-core') and a missing 'release-note' block in the PR description.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

fieldPath: metadata.uid
image: registry.k8s.io/cluster-api/cluster-api-controller:v1.10.1
imagePullPolicy: IfNotPresent
startupProbe:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

low

The pull request does not fully adhere to the repository's commit and PR requirements. First, the PR title/commit uses the scope 'capi', which is invalid; it should be 'cluster-api' or 'capi-providers-core'. Second, the PR description is missing the required 'release-note' block. Please update the PR title/commit scope and add the release-note block to the PR description.

References
  1. Each commit must follow Conventional Commits format with a valid scope (e.g., cluster-api or capi-providers-core). The PR body must contain a release-note block. (link)

@dosubot dosubot Bot added the area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) label Jun 17, 2026
@myasnikovdaniil
myasnikovdaniil marked this pull request as draft June 19, 2026 08:47
myasnikovdaniil added a commit that referenced this pull request Jun 22, 2026
## What

Consolidated landing branch for a set of interdependent platform/CI
stabilization fixes. It began as the source PRs in the table below (in
dependency order) and has since grown with review-driven fixes and a few
production-behavior changes — the full current inventory is under
**Scope** below. Landing them as one unit lets CI run against the real
combined state instead of a fragile bottom-up merge train.

## Source PRs (dependency order)

| # | Commit | Source PR | Addresses |
|---|--------|-----------|-----------|
| 1 | fix(capi): startupProbe on capi-controller-manager | #2946 | capi
crashloop during cert provisioning |
| 2 | test(e2e): bucket.bats port-forward + S3 client reliability |
#2944 | flaky bucket test |
| 3 | fix(seaweedfs): restore -lock BucketClass, s3 svc name, drop
volumeSizeLimitMB | #2943 | missing bucket/access classes |
| 4 | test(e2e): in-cluster Cilium endpoint-leak healer (install + apps)
| #2874 | cilium "IP already in use" leak |
| 5 | refactor(build): mode=max registry cache + main-only warmer |
#2938 | build cache |
| 6 | ci(build): isolate each PR build on its own ephemeral runner VM |
#2939 | shared-buildkit contention |
| 7 | fix(e2e): LINSTOR post-install waits on a single 15m deadline |
#2928 | LINSTOR provisioning timeouts |
| 8 | test(metallb): assert digest-pinned image form, not version
literal | #2873 | brittle metallb assertion |

## Why consolidated

These form a dependency DAG (verified from CI logs): e.g. 2943 needs
2946, 2938 needs 2874, 2928 needs 2938/2939, 2873 needs 2928. Tested
bottom-up, each lower PR runs with none of the fixes above it and can't
go green alone. This branch carries all of them, so CI runs against the
real combined state.

## Verification status

CI is green on the latest head (`5653c30`): full-suite E2E passes
end-to-end and is reproducible (2 of 3 runs on this SHA green). The one
red run was a LINSTOR tie-breaker / DRBD-metadata infra flake on the
sandbox, unrelated to the diff — a different environmental subsystem
failed each run (details in the comments below). The earlier 3-hour
crust-gather snapshot hang is fixed in `5653c30`.

## Scope beyond the original 8

The branch has grown past the 8 source PRs above with review-driven
fixes and a few production-behavior changes. For reviewer transparency,
the full set:

**Review fixes (@lexfrei review):**
- **B1 (blocker)** — `8f41910` converts the SeaweedFS
`-lock`/`-readonly` BucketClasses and the s3 service-name override into
`patches/`, wired into `make update`, so a re-vendor no longer drops
them.
- **FU1** — `cc99230` grants the cilium leak-healer `delete` on pods
(both delete remedies were RBAC-forbidden).
- **FU3** — `014de3f` adds a unit test for the HelmRelease update
conflict-retry path.
- **FU4** — `6deddc6` makes the leak-healer refuse a disconnect for any
non-terminal owner phase, not just `Running`.
- **FU7** — `f3914d2` sets `persist-credentials: false` on the
build-cache checkout (SHA-pinning is handled repo-wide in #2849).

**Production-behavior changes (not in the table above):**
- VPA `updateMode: Auto → Initial` for etcd (`6e9ff90`) and monitoring
(`0997105`), plus `vmselect`/`vmstorage` `minAllowed` floors — stops
install-time eviction churn.
- cozystack-api: `retry.RetryOnConflict` on the Application→HelmRelease
update path (`728f676`).

**E2E robustness follow-ups:** per-test crust-gather snapshots
(`50e5f94`), EtcdBackupSchedule wait (`763b85e`), tenant-node
single-deadline wait (`9e43d45`), harbor BucketClaim 10m budget
(`d7f244f`), tenant API via LoadBalancer (`9beda4e`), crust-gather pin
(`95f42aa`).

**Not addressed (by decision):** FU2 — the s3 Service keeps
`seaweedfs.name`-based naming via patch (rendered output unchanged;
cozystack renders `fullname == seaweedfs`, so it matches the
`componentName`/`fullname` siblings in practice). FU6 — fork PR build
push is handled by the existing mirror-to-same-repo-branch workflow.

## Relates to

#2946 #2944 #2943 #2874 #2938 #2939 #2928 #2873 — if this lands, those
can be closed; otherwise they remain the granular per-PR review path.


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

* **New Features**
  * Added CI build cache warming for main branch builds.
* Added a best-effort in-cluster Cilium endpoint self-heal watchdog for
e2e installs.
* Extended SeaweedFS COSI with object-lock support plus separate
readonly access.

* **Improvements**
* Reduced e2e install flakiness with a shared readiness deadline, safer
waits, and improved cleanup/diagnostics capture.
* Prevented install-time churn by switching VPA update mode to
**Initial**.
  * Added container startup probes and updated SeaweedFS volume sizing.

* **Tests**
* Improved e2e robustness (etcd backup schedule waiting, digest-pinned
image checks, and corrected S3 port-forwarding).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
pull Bot pushed a commit to medampudi/cozystack that referenced this pull request Jun 22, 2026
…t provisioning delay (cozystack#2946)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@myasnikovdaniil

Copy link
Copy Markdown
Contributor Author

superseded by #2948

Marian Koreniuk (themoriarti) added a commit to themoriarti/cozystack that referenced this pull request Sep 23, 2026
Each provider package ships the manifests twice: the YAML a reviewer reads and
the gzip blob the chart mounts. `make components` writes both from one download
and nothing keeps them together afterwards, so editing the YAML alone produces a
diff that reviews cleanly while the cluster runs the version nobody read.

Three packages are covered, capi-providers-core among them: it has no
`make components` target at all, both of its files are hand maintained, and
cozystack#2946 edited only the YAML.

A second pair of tests holds the version together. capi-operator hashes the
provider spec and the config Secret into applied-spec-hash, and the ConfigMap
is in neither, so a bump that regenerates the vendored files while the
ConfigMap name and spec.version stay behind installs the old provider and
reports the old version, with nothing reporting an error. The Makefile
variable, the ConfigMap name and spec.version have to be one value.

Written for dash rather than bash, since the repo's runner executes these under
sh — process substitution was the first thing it rejected.

Shown to fail on a YAML edited without regenerating the blob, on a Makefile
version bumped on its own, and on a ConfigMap renamed without spec.version.

Signed-off-by: Marian Koreniuk <[email protected]>
Assisted-by: LLM
Marian Koreniuk (themoriarti) added a commit to themoriarti/cozystack that referenced this pull request Sep 23, 2026
…olds

cozystack#2946 added a startupProbe to capi-controller-manager so it survives the
certificate provisioning delay. It edited
capi-providers-core/files/core-components.yaml, which is the copy a reviewer
reads. The chart mounts files/components.gz, and that blob was never
regenerated, so every install since has run the controller without the probe
and nothing reported it.

capi-providers-core has no `make components` target — both files are hand
maintained — so the blob is regenerated here straight from the YAML:

  gzip -9 -n -c files/core-components.yaml > files/components.gz

The decompressed blob now matches the YAML byte for byte, and the only change
it carries is the six lines of cozystack#2946.
hack/capi-provider-components_test.bats covers this package from now on, so
the next edit of one file without the other fails in CI.

Its .helmignore is corrected in the same pass: `files/.*-components.yaml` is a
glob, so it matched only names beginning with a dot, and `helm package` shipped
the readable YAML alongside the blob that duplicates it. The two new provider
packages copied the same pattern and are fixed in their own commits.

Signed-off-by: Marian Koreniuk <[email protected]>
Assisted-by: LLM
Marian Koreniuk (themoriarti) added a commit to themoriarti/cozystack that referenced this pull request Sep 24, 2026
Each provider package ships the manifests twice: the YAML a reviewer reads and
the gzip blob the chart mounts. `make components` writes both from one download
and nothing keeps them together afterwards, so editing the YAML alone produces a
diff that reviews cleanly while the cluster runs the version nobody read.

Three packages are covered, capi-providers-core among them: it has no
`make components` target at all, both of its files are hand maintained, and
cozystack#2946 edited only the YAML.

A second pair of tests holds the version together. capi-operator hashes the
provider spec and the config Secret into applied-spec-hash, and the ConfigMap
is in neither, so a bump that regenerates the vendored files while the
ConfigMap name and spec.version stay behind installs the old provider and
reports the old version, with nothing reporting an error. The Makefile
variable, the ConfigMap name and spec.version have to be one value.

Written for dash rather than bash, since the repo's runner executes these under
sh — process substitution was the first thing it rejected.

Shown to fail on a YAML edited without regenerating the blob, on a Makefile
version bumped on its own, and on a ConfigMap renamed without spec.version.

Signed-off-by: Marian Koreniuk <[email protected]>
Assisted-by: LLM
Marian Koreniuk (themoriarti) added a commit to themoriarti/cozystack that referenced this pull request Sep 24, 2026
…olds

cozystack#2946 added a startupProbe to capi-controller-manager so it survives the
certificate provisioning delay. It edited
capi-providers-core/files/core-components.yaml, which is the copy a reviewer
reads. The chart mounts files/components.gz, and that blob was never
regenerated, so every install since has run the controller without the probe
and nothing reported it.

capi-providers-core has no `make components` target — both files are hand
maintained — so the blob is regenerated here straight from the YAML:

  gzip -9 -n -c files/core-components.yaml > files/components.gz

The decompressed blob now matches the YAML byte for byte, and the only change
it carries is the six lines of cozystack#2946.
hack/capi-provider-components_test.bats covers this package from now on, so
the next edit of one file without the other fails in CI.

Its .helmignore is corrected in the same pass: `files/.*-components.yaml` is a
glob, so it matched only names beginning with a dot, and `helm package` shipped
the readable YAML alongside the blob that duplicates it. The two new provider
packages copied the same pattern and are fixed in their own commits.

Signed-off-by: Marian Koreniuk <[email protected]>
Assisted-by: LLM
Aleksei Sviridkin (lexfrei) pushed a commit that referenced this pull request Sep 25, 2026
…olds

#2946 added a startupProbe to capi-controller-manager so it survives the
certificate provisioning delay. It edited
capi-providers-core/files/core-components.yaml, which is the copy a reviewer
reads. The chart mounts files/components.gz, and that blob was never
regenerated, so the probe never reached a cluster.

The blob is regenerated from the YAML it is supposed to carry:

  gzip -9 -n -c files/core-components.yaml > files/components.gz

The decompressed blob now matches the YAML byte for byte, and the only change
it carries is the six lines of #2946.

This reaches new installs only. capi-operator re-applies a provider when the
hash of its spec and its config Secret changes; the ConfigMap holding the
components is in neither, so a cluster that already runs capi-providers-core
keeps its manager without the probe until the provider's version moves.
Whether existing clusters should get the probe, and how, is left open here on
purpose: moving spec.version and the ConfigMap name together would force the
re-apply, but that is a version decision for the package, not a side effect of
regenerating a blob.

.helmignore is corrected in the same change: `files/.*-components.yaml` is a
glob, so it matched only names beginning with a dot, and `helm package`
shipped the readable YAML alongside the blob that duplicates it.

hack/capi-core-components_test.bats holds the two files to the same bytes from
now on, so the next edit of one without the other fails in CI.

Signed-off-by: Marian Koreniuk <[email protected]>
Assisted-by: LLM
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Sep 25, 2026
…olds (#4483)

## What this PR does

`capi-providers-core` ships the Cluster API core manifests twice: as
`files/core-components.yaml`, which a reviewer reads, and as
`files/components.gz`, which the chart mounts into the cluster. #2946
added a `startupProbe` to `capi-controller-manager` and edited only the
YAML; the blob was never regenerated, so the probe never reached a
cluster. This regenerates the blob from the YAML (`gzip -9 -n -c
files/core-components.yaml > files/components.gz`), so the decompressed
blob matches the YAML byte for byte, and the only change it carries is
the six lines of #2946.

### What this reaches, and what it does not

New installs only. capi-operator re-applies a provider when the hash of
its spec and its config Secret changes (`applied-spec-hash`); the
ConfigMap holding the components is in neither, so a cluster that
already runs `capi-providers-core` keeps its manager without the probe
until the provider's version moves. Whether existing clusters should get
the probe, and how, is left open on purpose: moving `spec.version` and
the ConfigMap name together would force the re-apply, but that is a
version decision for the package, not a side effect of regenerating a
blob.

### Also in this diff

- `.helmignore`: `files/.*-components.yaml` is a glob, so it matched
only names beginning with a dot, and `helm package` shipped the readable
YAML alongside the blob that duplicates it. Now
`files/*-components.yaml`.
- `hack/capi-core-components_test.bats` holds the two files to the same
bytes from now on; it goes red on a YAML edited without regenerating the
blob.

Found by the drift check written for the Proxmox provider packages in
#4087, pointed at the package that was already here; split out because
it changes what every new install runs and deserves its own release
note.

### Testing

`bats hack/capi-core-components_test.bats`: 1/1; red with a line
appended to the YAML, green again after `gzip -9 -n -c`. `helm package`
of the chart now contains `files/components.gz` and
`files/metadata.yaml` only.

### Screenshots

Not applicable, no UI change.

### Downstream repositories

- [x] No downstream repository is affected by this change

### Release note

```release-note
fix(cluster-api): ship the `startupProbe` from #2946 in the packaged core provider components. New installs get it; a cluster that already runs the core provider keeps its manager without the probe until the provider's version moves, because capi-operator re-applies components only when the provider spec or its config Secret changes.
```


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

## Summary by CodeRabbit

* **Tests**
* Added a check that the compressed core components match the YAML
source, with a diff provided when they differ.
* **Chores**
* Updated chart packaging exclusions to cover component YAML files
regardless of whether their names begin with a dot.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Sep 25, 2026
…ster IPAM as optional packages (#4087)

## What this PR does

**Series behind #3481** — three separable pull requests:

1. **#4087 — Cluster API providers** (capmox + in-cluster IPAM)
2. #4088 — the `substrate` switch in `kubernetes` and `kubernetes-nodes`
3. #4089 — the Proxmox CSI driver and cloud-controller-manager

#4087 and #4088 share no files and can be reviewed in parallel. #4089
builds on #4088.

---

This is the provisioning layer #3481 asks to specify first: the part
that
creates the VMs. Neither of the other two is needed to review it.

### What it adds

Two Cluster API providers, packaged the way the existing KubeVirt
provider is —
vendored components, `gzip -9 -n` for a reproducible artifact, a `make
components` target that regenerates it from the upstream release and
checks
the downloads against the SHA-256 sums the Makefile pins:

| Package | Version | Contract |
|---|---|---|
| `capi-providers-infraprovider-proxmox` | capmox v0.7.7 | v1beta1 |
| `capi-providers-ipam-in-cluster` | CAIP v1.0.3 | v1beta1 |

capmox v0.7.7 rather than the current v0.9.x on purpose: from v0.8 it
declares
contract `v1beta2`, which the CAPI core shipped here (v1.10.1) does not
serve.

Both join the `iaas` bundle as **opt-in** packages, where the KubeVirt
infrastructure provider is unconditional. The asymmetry is deliberate:
capmox
drives a hypervisor outside the cluster and needs a Secret holding a
Proxmox API
token, which capi-operator reads before it installs anything — with no
Secret the
provider is never deployed at all. Enabling it by default would ask
every
Cozystack install that has no Proxmox to supply hypervisor credentials.

```yaml
bundles:
  enabledPackages:
    - cozystack.capi-provider-infra-proxmox     # brings IPAM with it
    - cozystack.capi-provider-ipam-in-cluster   # also enableable on its own
```

Enabling capmox emits the IPAM provider automatically — capmox allocates
worker
addresses from an `InClusterIPPool` and has no DHCP mode, so on its own
it never
gets a `ProxmoxCluster` to `Ready`. IPAM stays independently enableable,
since
assigning addresses is a generic Cluster API concern.

Naming capmox in `enabledPackages` while disabling IPAM stops the
render. The
cluster would catch that combination on its own — the capmox
`PackageSource`
depends on the IPAM `Package`, so with that `Package` absent capmox goes
`Ready=False` with `DependenciesNotReady` and no `HelmRelease` is
created — but
it would not say why. Failing the render names the two conflicting list
entries
at the moment someone writes them, instead of leaving a condition to
decode.

### Why the earlier attempt did not produce a VM

#1927's blocker — "ProxmoxMachine does not produce a VM" — was not a
capmox
incompatibility. It was four independent causes, each of which only
became
visible once the previous one was cleared:

1. **`PROXMOX_URL` included `/api2/json`.** capmox appends that itself,
so every
   call went to `/api2/json/api2/json/…` and returned 501.
2. **No IPAM provider.** `no matches for kind "InClusterIPPool"`; the
`ProxmoxCluster` never reached `Ready` and every `ProxmoxMachine` sat in
   `WaitingForClusterInfrastructure`.
3. **`templateSelector.matchTags` is set equality, not containment**
(`slices.Equal` after sort). A template carrying four tags is not
selected by
   naming one of them: `found 0 VM templates with tags`.
4. **capmox waits for the qemu-guest-agent, which Talos does not ship.**
   `error waiting for agent: timed out` — cleared with
   `checks.skipQemuGuestAgent` and `skipCloudInitStatus`.

None of these are in the provider. They are packaging and configuration,
which
is why the earlier work could look complete and still never boot a
machine.

### Evidence

Run on Proxmox VE 9.2.11 with Talos v1.13.6 and Cozystack's CAPI stack
(core v1.10.1, CABPT v0.6.12, Kamaji control-plane provider v0.19.0):

```
$ kubectl get proxmoxmachine,machine -n tenant-proxmox
proxmoxmachine/kubernetes-px-md0-…   Ready=True   VMProvisioned=True
machine/kubernetes-px-md0-…          Running      proxmox://0ef88cc1-6aea-4563-af64-344d8060d5c0

$ qm list
106 kubernetes-px-md0-jmzds-h9npx  running
```

The machine boots Talos from the nocloud ISO, reads the machineconfig
capmox
writes, and joins. That is the layer #69 called a hard blocker.

### What this PR does not claim

- One Proxmox node, one tenant cluster, manual runs — there is no e2e
coverage
  for Proxmox in CI, and this PR does not add any.
- It does not make Proxmox a supported platform. It makes the
provisioning layer
reviewable, which #3481 names as the precondition for that conversation.
- It does not touch `capi-providers-core`. The same drift check, pointed
at that
package, shows that the `startupProbe` from #2946 never reached the
shipped
blob; that fix changes what every new install runs and is #4483, with
its
  own release note.
- Nothing here exposes `ProxmoxCluster.spec.credentialsRef` to a tenant,
and
nothing should: `capmox-manager-role` grants get/list/watch/patch on
Secrets
  cluster-wide and that field takes a namespace. Whatever renders a
`ProxmoxCluster` for a tenant in the rest of the series has to keep the
field
  out of tenant hands.

### Testing

`make unit-tests` — exit 0. New suites:
`packages/core/platform/tests/sources_capi_provider_proxmox_test.yaml`
and
`bundles_proxmox_wiring_test.yaml` (both bundle paths, the
enable/disable
precedence, and the refusal above).

Every negated `containsDocument` in the wiring suite carries `any:
true`;
without it the negation passes whether or not the document is in the
render.
Three mutations, each reverted afterwards — emitting the capmox
`Package`
unconditionally, emitting the IPAM `Package` unconditionally, and
dropping the
`disabledPackages` check from `$proxmoxEnabled` — each turn red the
tests named
after the property they break, and the unmutated chart stays green (44
suites,
198 tests).

`hack/capi-provider-components_test.bats` covers both new provider
packages:
the blob and the YAML are the same bytes; the Makefile version, the
ConfigMap
name and `spec.version` are one value — `applied-spec-hash` covers the
provider
spec and the config Secret but not the ConfigMap, so a bump that
regenerates the
files while `spec.version` stays behind installs the old provider
silently; and
the vendored files match the SHA-256 sums the Makefile pins, so the pin
holds
even when nobody runs `make components`.

The render was compared against `main` for five configurations
(defaults,
`isp-full`, `isp-full-generic` with paas, an unrelated
`enabledPackages`, and
`disabledPackages` naming IPAM). No line is removed in any of them and
the
`Package` count is unchanged; the only additions are the two
`PackageSource`
objects, which the chart renders for every package, optional or not.

### Screenshots

Not applicable — no UI change.

### Downstream repositories

I walked the trigger map in `docs/agents/contributing.md` against this
diff, file by file. What it matches is below. **No box is ticked**,
because every follow-up it points at depends on a decision that is not
mine to make: #3481 asks whether Proxmox becomes a supported platform at
all, and writing its documentation or its Terraform schema before that
is answered would be work built on an assumption. I have not filed
speculative PRs or issues in those repositories for the same reason. If
the direction is accepted, I open them; a human should decide which.

**cozystack/website — matches, no follow-up yet.**

- `packages/core/platform/values.yaml` changed.
`content/en/docs/next/operations/configuration/platform-package.md` is a
hand-written table of the `spec.components.platform.values.*` keys; this
PR does not add a key, but it documents two new values that
`bundles.enabledPackages` now accepts, and the table is where a reader
would look for them.
- Two platform components are added
(`capi-providers-infraprovider-proxmox`,
`capi-providers-ipam-in-cluster`) → `guides/platform-stack/_index.md`,
`operations/configuration/licenses.md`.
- The `iaas` bundle gains two optional packages →
`operations/configuration/variants.md`.

**Everything else: no match.** Nothing under `packages/apps/` or
`packages/extra/` (terraform-provider, website app lists), no
`packages/core/installer/values.yaml` or `networking.*`/`publishing.*`
key the Ansible role sets, no `hack/` file or package-layout change
(ccp, external-apps-example), no `ApplicationDefinition` semantics, no
telemetry metric, no node prerequisites (talm, ansible), no
commit-convention change (community).

- [ ] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

```release-note
feat(cluster-api): add optional Cluster API packages for Proxmox — the capmox infrastructure provider and the in-cluster IPAM provider it requires. Neither is deployed unless named in `bundles.enabledPackages`, since capmox drives a hypervisor outside the cluster and does nothing without Proxmox API credentials. Enabling the Proxmox provider brings the IPAM provider with it.
```


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

- **New Features**
- Added optional Proxmox infrastructure provider support for IaaS
clusters.
- Added in-cluster IP address management, usable independently or
alongside Proxmox.
- Proxmox deployments require a pre-created credentials Secret and
automatically include in-cluster IPAM.
  - Added validation for incompatible Proxmox and IPAM settings.

- **Tests**
- Added coverage for provider dependencies, package enablement,
disabling behavior, invalid configurations, and packaged provider
components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/bug Categorizes issue or PR as related to a bug size/XS This PR changes 0-9 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant