mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +09:00
6f3d26c4914bb024deaeada3fd96633fa25b6f4c
7851
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6f3d26c491 |
feat(backups): tell on the BackupJob why a Redis artifact was not recorded
A Backup without status.artifact looked the same whether a custom strategy wrote elsewhere, the credentials Secret was unusable or the endpoint CA was gone; only the controller log told them apart. The probe now returns why it gave up, and the controller records it as a Normal ArtifactNotMeasured Event on the BackupJob. The Event lives in the application namespace, so it carries a fixed reason only; the raw error, which may name the platform's endpoint and bucket, stays in the log. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
d61a5054a1 |
fix(backups): refuse to probe a Redis snapshot behind a CA that is gone
The default strategy mounts the s3-ca volume only when the admin configured an endpoint CA, and the script then trusts that CA alone. When the volume yields no CA by the time the probe runs, because the Secret was deleted or emptied after the Job or the volume has another source, the probe used to fall back to the system roots, trusting more than the Job may have. It now leaves the artifact unset instead, with or without skip-verify. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
43bed8f6d9 |
fix(backups): keep the Redis probe verifying TLS unless every container skips
The probe read S3_INSECURE_SKIP_VERIFY from any container of the backup Job, so a custom strategy whose sidecar sets it while the uploading container verifies made the probe skip verification the upload did not. The probe cannot tell which container uploaded, so it now skips only when every container sets the variable to true, the last entry winning as in the kubelet. An s3-ca volume of a source other than a Secret yields no CA the probe can read; it is now treated like a CA Secret that is gone, so it never turns into skip-verify. Tests pin the guards that decide the probe's trust: the Redis CA volume mounted next to the S3 one, a decoy variable set to true, a CA that did not sign the endpoint, and the AlreadyExists read-back past the cache. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
e03986e8d0 |
fix(backups): treat an S3 object without a reported size as unmeasured
minio-go returns size -1 when a HEAD answer carries no Content-Length, which would land in Backup.status.artifact.sizeBytes as -1. The probe now reports such an object as unmeasured, so the field stays unset. The shared client's trust settings also gain a test against an endpoint whose certificate the configured CA did not sign, with the CA alone and on top of the system roots: nothing pinned that a configured CA is actually enforced. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
631f6cfc0d |
fix(backups): mirror the Redis script's CA trust exactly
The script passes the endpoint CA as curl --cacert, which replaces the default trust store, while the probe trusted the system roots on top of it. Trust only the CA, as the script does. A mounted CA Secret that is still there but no longer carries ca.crt or tls.crt let the probe skip TLS verification when the strategy opts into it, although the Job may have verified against a key since removed. Treat a volume that yields no CA now like a vanished Secret and refuse to skip verification in its place. Pin the probe timeout and the reconcile's retry of an unreadable Backup for Redis with tests. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
50b1a2e5ee |
test(backups): pin the Redis retry check
Cover the two retry-check paths the Redis driver shares with MariaDB and CNPG: a failed existence check creates the Backup without probing the bucket, and an existing Backup that cannot be read back is returned as errBackupNotReadBack so the reconcile is retried. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
bfea468b6c |
fix(backups): never skip TLS verification in place of a vanished CA
The Redis probe reads the CA Secret when it runs, not when the backup Job did. If the pod mounted a CA Secret that has since been deleted and the strategy also opts into S3_INSECURE_SKIP_VERIFY, the probe skipped verification against an endpoint the Job may have verified. Refuse to probe in that case. Without skip-verify a missing CA Secret still falls back to the system roots, as the script does. The endpoint comment claimed a bare host; it can carry a path, which the shared client now refuses. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
2ff547348d |
fix(backups): probe the Redis snapshot with the trust its Job used
The default Redis strategy script verifies the S3 endpoint against ca.crt, then tls.crt, from the Secret mounted as the s3-ca volume (backupStorage.endpointCASecretName), and only without either honours S3_INSECURE_SKIP_VERIFY=true. The probe always verified against the system roots, so on a cluster with a private CA, such as a self-signed SeaweedFS, every Redis and Valkey backup succeeded with no artifact. Read the same settings from the pod spec of the completed backup Job, in the order the script applies them, and pass them to the S3 connection. The shared transport gains an InsecureSkipVerify switch that is set only from that explicit opt-in. The projector always stores the endpoint as a bare host, so the branch that parsed a scheme out of it was unreachable; drop it and its test. Cover the endpoint and access-key terms of the Secret completeness check. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
c8cdefc665 |
feat(backups): record the RDB snapshot location and size for Redis backups
Backups made by the Redis strategy, which also serves Valkey, carried no Backup.status.artifact at all: neither a URI nor a size. The snapshot is one object per backup, <namespace>/<app>/<backup>.rdb, whose key the driver already records, and the default strategy uploads it with the S3 coordinates of the backup credentials Secret the controller projects into the application namespace. On completion the driver now stats that object with the same coordinates and records its s3:// URI and size. The artifact is set only when the object is found, so a custom strategy that writes elsewhere gets no URI pointing at a missing object. A Backup that already exists is returned without probing, as for the MariaDB and CNPG drivers. Assisted-by: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
5e9c392352 |
feat(api): add the reservation evaluator for tenant quotas (#4983)
## What this PR does First phase of the accepted [tenant quotas as reservation limits](https://github.com/cozystack/community/pull/48) design: a standalone, importable `pkg/reservation` package that computes what an application consumes from a tenant's budget from its declared values. Nothing calls it yet; the usage oracle, the generalized admission gate and the per-kind declarations come in the following phases. Today a tenant quota is declared in instance-type units but enforced against `ResourceQuota.status.used`, which counts virt-launcher pods with their additive virtualization overhead and drifts without recomputation. The design makes the declared reservation the accounting authority instead, and this package is the evaluator both the apiserver and the controller will read. - **Declarations are data, not Go.** A kind describes its consumption as CEL over its values (`exempt`, ordered `let` bindings, one expression per quota key), so the evaluator holds no per-kind knowledge and a kind from an out-of-tree catalog participates without a change here. The `Declaration` type is ready to be embedded in `ApplicationDefinitionSpec` in phase 2. Environment functions are `instanceType(name)`, `preset(name)` and `quantity(x)`, on top of the Kubernetes CEL quantity library; scaling is `quantity.mul(n)`, because the standard `_*_` cannot take a quantity overload. - **Instance types resolve to scalars keyed by class**, read from the type's spec and never from its name: `cpu`, `dedicated-cpu` (guest cores plus the isolated emulator thread, the extra parity core when the type itself carries the `EmulatorThreadCompleteToEvenParity` annotation, and a supplemental I/O pool), `memory`, `hugepages-<size>`, `overcommitted-memory`. The test pins the classification against every `VirtualMachineClusterInstancetype` shipped in `packages/system/kubevirt-instancetypes`. - **Fail closed.** An unknown instance type or preset, a value of the wrong type, an unguarded absent key, an unparseable quantity or a negative figure is an error, and values are checked against the kind's schema before defaulting, which would otherwise replace a mistyped value with its default, never a smaller reservation. A definition that declares neither its consumption nor an exemption yields `ErrNoDeclaration`. - **Stored values are defaulted first.** The aggregated apiserver persists a tenant's spec without its schema defaults, so an evaluator reading them raw would count zero for `kubernetes-nodes.maxReplicas` on every pool created with defaults. The structural-schema defaulting moves from `pkg/registry/apps/application` into `pkg/apis/apps/defaulting` so the registry and the evaluator apply the same defaults; the registry now only delegates to it, with no behaviour change. - **Aggregation** sums reservations per namespace and returns the applications that could not be evaluated separately, so a caller never mistakes an incomplete sum for a complete one. The evaluator tests run against the shipped OpenAPI schemas of `postgres`, `vm-instance` and `kubernetes-nodes`: the cozy-lib per-key preset fill, a `{cpu: 4, sockets: 2}` block reserving 8 vCPUs, the autoscaling ceiling, `external: true` reserving a load balancer, and a ComputePlane-shaped `KubernetesNodes` release carrying only `{roles, minReplicas}` reserving exactly what the same pool with its defaults written out reserves. Left for the next phases on purpose: where presets are resolved from (the design's direction is platform-shipped environment objects; here they come through the `Environment` interface), the `ApplicationDefinition` field itself, and any consumer. ### Screenshots Not a UI change. ### Downstream repositories The diff adds two Go packages and moves the defaulting helpers without changing behaviour. It touches no package under `packages/`, no `values.yaml`, no CRD and no `ApplicationDefinition` field, so none of the triggers in the map is reached. - [x] 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(api): add the reservation evaluator that computes an application's tenant-quota consumption from its declared values. Internal only, with no consumer yet and no change in behaviour. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit - **New Features** - Added reservation evaluation for application resources, including CPU, memory, hugepages, and overcommitted memory. - Added support for instance types, resource presets, schema-based defaults, and CEL expressions. - Added namespace-level reservation aggregation with clear reporting for applications that cannot be evaluated. - Added validation for invalid, negative, unsupported, or excessive resource values. - Added classification of KubeVirt instance types, including dedicated CPUs, emulator threads, supplemental I/O threads, and hugepages. - **Bug Fixes** - Schema defaults are applied consistently to nested application values. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b53ca1fede |
fix(postgres): refuse system databases and never drop them (#5155)
<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes are not an exhaustive list — pick the most specific scope for
the change and extend the list when a genuinely new area appears.
Examples:
- System components: dashboard, platform, operator, cilium, kube-ovn,
linstor, fluxcd, cluster-api
- Managed apps: postgres, mariadb, redis, kafka, clickhouse,
virtual-machine, kubernetes
- Development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or
add a `BREAKING CHANGE:` footer
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `kind/backport` if it's a bugfix that needs to be
backported to a previous version.
-->
## What this PR does
`databases` accepts `postgres`, `template0` and `template1`, and the
init job treats them like application databases: it comments them
`database managed by helm`, creates `<db>_admin` and `<db>_readonly`,
makes `<db>_admin` the owner of the database and its schemas, and
installs the `trigger_auto_grant` event trigger. When the entry later
leaves values, the delete pass runs `DROP DATABASE` on it, which can
only fail:
- `template1`, `template0`: `cannot drop a template database`;
- `postgres`: `cannot drop the currently open database`, since the job
is connected to it.
The init job then fails on every upgrade, so the HelmRelease sits in
`UpgradeFailed` with `post-upgrade hooks failed`, and putting the entry
back does not help. `template1` also hands the takeover (owner of
`public`, default privileges, trigger) to every database created
afterwards. Once its roles are dropped by hand, the trigger left behind
names a role that no longer exists, so `CREATE SCHEMA` fails in every
new database, even as superuser.
This PR:
- **Refuses** the three names at render with a message that says what to
do. `cozyvalues-gen` has no way to constrain the keys of a
`map[string]Database`, so the refusal sits next to the existing
database-name guard, like the `users.postgres` refusal. The field
description says it too.
- **Never selects them for `DROP DATABASE`.** The delete pass excludes
them whatever values say.
- **Hands a taken-over system database back.** In a new `release system
databases` pass before the delete pass, for each of the three still
commented `database managed by helm`: drop the trigger and
`auto_grant_schema_privileges()`, `REASSIGN OWNED` / `DROP OWNED` for
its two roles in that database, give `public` back to
`pg_database_owner` (PostgreSQL 15+), and clear the comment. The
existing orphaned-roles pass then drops the roles; it never visits
`template1`, hence the per-database step. `template0` accepts no
connection and the job that commented it never got further, so only its
comment is cleared.
**Upgrade note.** A release that declares one of the three names fails
to render until the entry is removed. Removing it is now safe: the next
run releases the database instead of dropping it.
**Testing**
- `tests/system_databases_test.yaml`: 5 of its 6 cases fail on `main`
without the change. `helm unittest packages/apps/postgres`: 215 passed.
- I replayed the scenario against a `postgres:17` container, running the
rendered `init.sh` with `PGUSER=postgres`:
1. The `main` script with `databases: {appdb, template1, postgres}`
reproduces the takeover.
2. The `main` script with only `appdb` then fails with `cannot drop the
currently open database`.
3. This branch's script on that same state exits 0. It releases both
databases (owner `postgres`, no comment, no event trigger, `public`
owned by `pg_database_owner`) and drops `postgres_admin`,
`postgres_readonly`, `template1_admin` and `template1_readonly`. A
database created afterwards gets no trigger, and `CREATE SCHEMA`
succeeds in it.
4. A second run is a no-op.
5. A `template0` commented and given roles by hand is released the same
way.
Open PR #5091 touches the same script (stable database order); the hunks
do not overlap.
Backport: the automatic cherry-pick onto `release-1.6` conflicts in
`init-script.yaml` and the generated cozyrd, so the backport to
`release-1.6` is done by hand in #5157 (same SQL, replayed against that
branch's script). `release-1.7` keeps `kind/backport`.
### Screenshots
<!-- REQUIRED for UI changes: attach screenshots or screen recordings
demonstrating
the visual impact of your changes. PRs with UI changes without
screenshots will not be merged. -->
### Downstream repositories
<!--
Cozystack is upstream for repositories that are NOT kept in sync with it
automatically, and nothing in CI compares the two sides, so a change
here can break them silently.
Which change forces what, and which file to touch, is the trigger map in
docs/agents/contributing.md under "Downstream Repositories". Walk it
before you tick anything.
IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM
TO FILL IN:
1. Walk the trigger map against the actual diff of this PR, file by
file. Do not decide from the PR title or from your memory of what you
changed.
2. For every repository you tick, open the follow-up PR there and link
it on the same line. A ticked box with no link claims work that does not
exist, which is worse than an empty box. Search that repository for an
open PR or issue covering it first, and link that instead of filing a
duplicate.
3. When the follow-up is out of scope, or needs a decision that is not
yours to make, open an issue in that repository instead, link that, and
say so in the PR body. Do not push a speculative PR into another
repository just to fill a line here.
4. Do not tick "No downstream repository is affected" to make the
checklist go away. If you are unsure, leave every box empty and say so
in the PR body, so a human decides.
-->
- [x] No downstream repository is affected by this change (walked the
map: a template guard, the init script, a test suite and one field
description regenerated into README, schema, Go types and cozyrd; no
field, default, enum or name 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
<!-- Write a release note:
- Explain what has changed internally and for users.
- Start with the same `type(scope):` prefix as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->
```release-note
fix(postgres): databases now refuses the PostgreSQL system databases postgres, template0 and template1, and the init job never runs DROP DATABASE on them. A system database a previous version took over is handed back to the superuser on the next upgrade (event trigger removed, ownership and public schema restored, its <db>_admin and <db>_readonly roles dropped). A release that declares one of them must remove the entry before upgrading.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Behavior Changes**
* PostgreSQL system databases (`postgres`, `template0`, and `template1`)
are refused as managed application databases.
* Previously managed system databases are released during cleanup and
excluded from database deletion. Cleanup restores eligible ownership and
security settings and clears system database comments.
* **Documentation**
* Updated PostgreSQL configuration guidance to clarify that these system
database names are refused. Names that merely contain these strings
remain accepted.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
10e0ef9ece |
fix(postgres): fail a backup job as soon as its cnpg Backup fails (#5211)
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `kind/backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does Part of #5201. The remaining part, a bound on a Backup that stays `pending`, is in #5209. A Postgres BackupJob whose `cnpg.io/Backup` had failed stayed `Running` for 30 minutes with reason `CNPGBackupTransientFailure`, on the premise that CloudNativePG may move a Backup out of the failed phase. It does not: in CloudNativePG 1.30.0 the backup controller returns early on any Backup already `failed` or `completed`, so the wait only delayed the verdict. The BackupJob now fails as soon as the Backup is `failed`, with the error CloudNativePG recorded. When that error is only the plugin's exit status, the message points to the `plugin-barman-cloud` sidecar log, where the storage error (`AccessDenied`, a connect timeout) is. A Backup in any other phase keeps the BackupJob waiting as before, but the BackupJob now carries a `Ready=False` / `WaitingForCNPGBackup` condition naming the Backup and its phase instead of no condition at all. These phases stay uncapped on purpose: CloudNativePG keeps a healthy Backup `pending` while another backup of the same Cluster runs and while it restarts one on another instance, so a fixed deadline here would fail healthy backups, in particular scheduled ones that queue behind a long previous run. #5209 tracks bounding a Backup that is stuck rather than queued, together with the Plan concurrency that makes queueing common. A table-driven test drives the reconciler through each Backup phase and asserts the job phase, the message, the persisted condition, that an unchanged phase does not rewrite the BackupJob status, and that only a completed Backup produces a Cozystack `Backup`. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Downstream repositories <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [x] 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 <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note fix(postgres): a Postgres BackupJob now fails as soon as its CloudNativePG Backup fails, with the recorded error, instead of after 30 minutes, and a BackupJob waiting on a CloudNativePG Backup reports a WaitingForCNPGBackup condition with the Backup's phase. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * CNPG backups that enter a failed state now fail their BackupJob immediately, with the recorded error shown when available. * BackupJobs waiting for a CNPG backup to finish remain in a waiting state, even after the backup deadline; the deadline applies while waiting for the backup to be created. * Waiting status now includes a clear reason, and failure messages provide a sidecar-log hint when relevant. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
91f03dd90e |
fix(platform): remove the dead registries value and patch-containerd Secret (#5190)
Nothing reads the `patch-containerd` Secret: the kubeadm certs.d mount it fed is gone, and the v1.6.0 changelog already says the passthrough is removed. The `registries` value stayed in the Package spec and takes mirrors that do nothing, which is worse than no knob for someone setting up an air-gapped cluster. Removed both. A Package that still sets `registries` keeps installing, the platform chart has no values schema so the key is ignored. On upgrade the Secret in `cozy-system` is deleted by the release. Mirrors for tenant workers are the per-pool `talos.registryMirrors` of KubernetesNodes. I checked that nothing is left: `git grep` for `.Values.registries` and for `patch-containerd` outside the changelogs finds nothing but the kubernetes README sentence that says the Secret was removed. Full `make unit-tests` passes. Fixes https://github.com/cozystack/cozystack/issues/4006 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Configuration Changes** * The platform chart no longer supports the `registries` configuration option or generates containerd registry secrets. Mirror endpoint settings, including credentials and TLS verification options, are no longer applied through this chart. * Registry mirrors can be configured per worker pool using `talos.registryMirrors` on `KubernetesNodes`. Existing packages that set `registries` continue to install, but the setting is ignored. * **Documentation** * Updated guidance for configuring registry mirrors on worker pools. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
63abbc80ee |
fix(api): read pool usage under the quota key the controller counts (#5184)
## What this PR does Tenant quota admission does not see how much of an extended resource or hugepages the parent pool already uses, so a child tenant can be granted a share the parent has already spent. Admission reads that usage from `limits.<key>`, but Kubernetes reports it only under `requests.<key>`, so the value is always zero. Now each key is read where the quota controller counts it. cpu, memory and ephemeral-storage still read `limits.*`. The new tests fail on main. ### Screenshots ### Downstream repositories - [x] 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 fix(api): tenant quota admission now counts the parent pool's usage of extended resources and hugepages, so a child tenant can no longer get a share the parent has already used. After upgrading, updates to existing tenants may be rejected if their quota for these resources exceeds the parent's remaining budget. This includes updates that leave the quota unchanged. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * Quota checks now account for existing usage of extended resources, preventing child requests from exceeding the parent’s remaining quota. * Requests are allowed when current usage leaves enough quota available, and denied when usage has consumed the parent’s full quota. * Resource usage is matched against the appropriate quota category, including requests for extended resources and hugepages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
0a310cc507 |
fix(api): default into typed nil maps and slices
A nil map or slice held in an interface is not == nil, so the defaulting walk took it for a present value: writing the first default into a nil map panicked, and a nil slice kept no default. The registry always passes a decoded map, but Apply is now a shared entry point and nothing guarantees that for its other callers. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io> |
||
|
|
89aba6e104 |
feat(api): add the reservation evaluator for tenant quotas
Tenant quotas are declared in instance-type units but enforced against ResourceQuota.status.used, which counts virt-launcher pods with their additive virtualization overhead and drifts without recomputation. The accepted tenant-quota-reservation design makes the declared reservation the accounting authority instead; this is its first phase, the evaluator, with no consumer yet. A kind describes its consumption as CEL over its values, so the evaluator carries no per-kind knowledge and an out-of-tree kind participates without a change here. Instance types resolve to scalars keyed by class, read from the type's spec rather than its name, because a pinned core, a hugepage and an overcommitted page are not the same capacity as a shared vCPU or ordinary memory. KubeVirt pins the emulator thread's parity core per type, from the annotations the type hands to its VMs, so the classifier reads it there too. Anything that does not resolve is an error, never a smaller figure. Stored values carry only the keys a tenant wrote, so the evaluator defaults them first. The defaulting code moves out of the application registry into its own package so both read the same defaults. Defaulting replaces a value of the wrong type with the schema's default, which would turn a malformed spec into a smaller reservation, so values are type-checked against the schema before it runs. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io> |
||
|
|
f003851338 |
fix(postgres): refuse system databases and never drop them
postgres, template0 and template1 were accepted in databases and handled like an application database. The init job made <db>_admin their owner, installed the auto-grant event trigger in them and, once the entry left values, ran DROP DATABASE on them. That DROP can only fail (template databases cannot be dropped, and the job is connected to postgres), so every later upgrade failed in the init job and the release stayed in UpgradeFailed even after the entry was put back. template1 also passed the takeover on to every database created from it afterwards, where the trigger, naming a role about to be dropped, makes CREATE SCHEMA fail. A release a previous version let take one of them over has to be repaired without dropping it, and without touching what this chart did not create: a role or owner that merely carries an expected name may belong to a tenant or an administrator, and a role comment can be set by any CREATEROLE holder, so only the cozystack:managed mark is trusted. What the marked roles own goes to cozystack:orphaned rather than to the superuser, since a tenant's SECURITY DEFINER function would otherwise run with superuser rights. A failed lookup of the databases cloned from template1 has to stop the run before template1 stops being selected, or those clones would never be repaired. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io> |
||
|
|
1f35c26ece |
fix(lineage, kubernetes): resolve CAPK node VMs to their cluster (#5124)
<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes are not an exhaustive list — pick the most specific scope for
the change and extend the list when a genuinely new area appears.
Examples:
- System components: dashboard, platform, operator, cilium, kube-ovn,
linstor, fluxcd, cluster-api
- Managed apps: postgres, mariadb, redis, kafka, clickhouse,
virtual-machine, kubernetes
- Development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or
add a `BREAKING CHANGE:` footer
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `kind/backport` if it's a bugfix that needs to be
backported to a previous version.
-->
## What this PR does
The virt-launcher pods of Kubernetes worker nodes are never marked as
managed by the lineage webhook, so anything that relies on the webhook
having resolved them misses them. The visible symptom: a SecurityGroup
attached to a `Kubernetes` application labels the control-plane pods but
never the worker nodes, because the securitygroup-controller caches only
pods carrying `internal.cozystack.io/managed-by-cozystack=true`.
**Root cause.** The webhook is not skipped for these pods (they do not
carry the managed-by label, so the `objectSelector` lets them through);
it runs and fails to resolve their owner chain. The walk goes Pod →
VirtualMachineInstance → VirtualMachine and stops there: the Cluster API
KubeVirt provider creates the VirtualMachine with no ownerReference and
links it to its KubevirtMachine through the
`capk.cluster.x-k8s.io/kubevirt-machine-{name,namespace}` labels only.
With no ownerReference, no HelmRelease kind and no
`helm.toolkit.fluxcd.io/name` label on the VM, `WalkOwnershipGraph`
returns no ancestor, `computeLabels` returns nothing and the pod is
admitted unmanaged. The
`apps.cozystack.io/application.{group,kind,name}` labels the pods do
carry come from the `KubevirtMachineTemplate` in
`packages/apps/kubernetes-nodes/templates/nodegroup.yaml`, not from the
webhook.
**Measured on a v1.6.5 cluster.** Every Kubernetes cluster on it showed
the same shape (three clusters across two tenants). For one worker node:
the pod carries the three lineage labels but not `managed-by-cozystack`;
the pod is owned by its VMI and the VMI by its VM; the VM has no
ownerReferences, and its labels include
`capk.cluster.x-k8s.io/kubevirt-machine-name` and
`capk.cluster.x-k8s.io/kubevirt-machine-namespace` pointing at a
KubevirtMachine in the same namespace. From there ownerReferences run
KubevirtMachine → Machine → MachineSet → MachineDeployment → Cluster,
and the CAPI Cluster carries `helm.toolkit.fluxcd.io/name:
kubernetes-<name>`. The bridge is one hop. Setting
`managed-by-cozystack=true` by hand on a worker's virt-launcher pod made
the securitygroup-controller stamp the membership label within ten
seconds.
**Fix.** `WalkOwnershipGraph` now follows a VirtualMachine without
ownerReferences to the KubevirtMachine named by its CAPK labels, and
keeps walking from there. Node pods then resolve to the parent
`Kubernetes` application, which is the attribution the machine template
already hard-codes, and they get `managed-by-cozystack` like every other
managed pod. The bridge is only taken when the label names the VM's own
namespace: CAPK can target an external infrastructure cluster, and the
walk must never attribute an object across namespaces. The webhook's
ClusterRole already grants `get` on every resource, so no RBAC change is
needed.
**Why at the source rather than in the consumer.** Relaxing the
securitygroup-controller's pod cache to
`apps.cozystack.io/application.kind` Exists would also have fixed the
symptom, but it would leave every other consumer of the managed-by label
blind to node pods, and it would rest membership on labels the webhook
never vouched for. Stamping `managed-by-cozystack` from the machine
template was ruled out too: the `objectSelector` would then skip these
pods entirely, and they would lose the scheduling-class injection the
webhook performs on every pod.
**Blast radius.** Only objects below a CAPK VM change: the VMI, the
virt-launcher pods and the PVCs of the VM's `dataVolumeTemplates` now
get the lineage labels of their Kubernetes application. None of them
match a resource selector of the `Kubernetes` ApplicationDefinition, so
they get `tenantresource=false`. Nothing selects PVCs on
`application.kind=Kubernetes` (the Velero strategy selects `VMDisk`).
The `-userdata` cloud-init Secret CAPK creates is not affected either
way: at the pinned provider commit, `reconcileKubevirtBootstrapSecret`
creates it with the bootstrap Secret's labels and no ownerReferences, so
the walk reaches neither the VM nor the KubevirtMachine from it, before
or after this change. Existing node pods pick the labels up on their
next admission (recreation, live migration, or any update).
**Tests.** `pkg/lineage/capk_test.go` builds the observed chain in a
fake dynamic client and checks that a worker's virt-launcher pod
resolves to `Kubernetes/cluster-a`, that a VM pointing at a
KubevirtMachine in another namespace is not followed, that a VM without
CAPK labels still has no ancestor, and, in a table of negative cases,
that the CAPK labels are ignored on any object other than a
`kubevirt.io/v1` VirtualMachine (another kind of the `kubevirt.io`
group, a VirtualMachineInstance, a VirtualMachine of another group or
version). Weakening the kind and API version guard to match on either
field alone fails all four cases. With the bridge disabled, the first
test fails. No chainsaw case: the only suites that bring up worker VMs
are `kubernetes-latest` and `kubernetes-previous`, whose shared runner
script is already over its time budget, and the unit test covers the
walk exactly as it runs in the webhook.
### Screenshots
<!-- REQUIRED for UI changes: attach screenshots or screen recordings
demonstrating
the visual impact of your changes. PRs with UI changes without
screenshots will not be merged. -->
### Downstream repositories
<!--
Cozystack is upstream for repositories that are NOT kept in sync with it
automatically, and nothing in CI compares the two sides, so a change
here can break them silently.
Which change forces what, and which file to touch, is the trigger map in
docs/agents/contributing.md under "Downstream Repositories". Walk it
before you tick anything.
IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM
TO FILL IN:
1. Walk the trigger map against the actual diff of this PR, file by
file. Do not decide from the PR title or from your memory of what you
changed.
2. For every repository you tick, open the follow-up PR there and link
it on the same line. A ticked box with no link claims work that does not
exist, which is worse than an empty box. Search that repository for an
open PR or issue covering it first, and link that instead of filing a
duplicate.
3. When the follow-up is out of scope, or needs a decision that is not
yours to make, open an issue in that repository instead, link that, and
say so in the PR body. Do not push a speculative PR into another
repository just to fill a line here.
4. Do not tick "No downstream repository is affected" to make the
checklist go away. If you are unsure, leave every box empty and say so
in the PR body, so a human decides.
-->
- [x] 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
<!-- Write a release note:
- Explain what has changed internally and for users.
- Start with the same `type(scope):` prefix as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->
```release-note
fix(lineage, kubernetes): the virt-launcher pods of Kubernetes worker nodes are now resolved to their Kubernetes application and marked as managed by Cozystack, so a SecurityGroup attached to a Kubernetes cluster now applies to its worker nodes, not only to its control plane.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Lineage results now connect supported virtual machines to their
associated machine and Kubernetes application when the required
association labels are present.
* **Bug Fixes**
* Association tracing is limited to supported virtual machine resources
and machines in the same namespace. Unlabelled standalone virtual
machines and machines in another namespace do not gain ancestors through
this association.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
da7f2c2f50 |
fix(tests): keep two unit tests green after the release image restamp (#5180)
## What this PR does Same change as #5179, on `main`. Both tests pass here today, because `main` still carries the pre-release image pins; they fail on every release line as soon as the first rc is cut, since the `Prepare release` commit rewrites `packages/*/values.yaml` and two unit tests read exactly the fields it rewrites. On `release-1.7` this has kept `Unit & controller tests` red on every PR since v1.7.0-rc.3 ([job](https://github.com/cozystack/cozystack/actions/runs/41162009892/job/124421449910) on #5172), and `release-1.8` would repeat it. | test | reads | before the release build | after (release-1.7 today) | |---|---|---|---| | `hack/cni-plugins-staging-contract.bats` "the kube-ovn release the CNI plugin pin was checked against is the one vendored" | `.global.images.kubeovn.tag` in `packages/system/kubeovn/values.yaml` | `v1.15.26@sha256:…` | `v1.7.0@sha256:…` | | `packages/system/monitoring/tests/vtraces_collector_test.yaml` "pins the collector image through the cozy-lib.image helper" | `tracingCollector.image` | `docker.io/otel/opentelemetry-collector-contrib:0.136.0@sha256:…` | `ghcr.io/cozystack/cozystack/otelcol-tracing:v0.136.0-cozystack.0@sha256:…` | Changes: - The bats test takes the vendored kube-ovn release from `appVersion` in `packages/system/kubeovn/charts/kube-ovn/Chart.yaml`. That is the value `packages/system/kubeovn/Makefile` builds the image from, and a release build leaves it alone. The hint printed on a mismatch names the new file. - The helm unittest case no longer names the image at all. It sets `_cluster.images-registry` and asserts that the registry lands in front of the rendered image and that the image stays digest-pinned (the `grafana_upstream_image_plugins_test.yaml` pattern). Removing the `cozy-lib.image` call from the template makes it fail; the old regex did not. Same single commit as #5179; the only difference is the unrelated `DOC=` line next to the edit, kept as `main` has it. Verified locally on this branch: `bats hack/cni-plugins-staging-contract.bats` 10/10, `helm unittest packages/system/monitoring -f tests/vtraces_collector_test.yaml` 28/28. Refs #5178 (the release-1.7 report), #5179, #5172. ### Downstream repositories - [x] No downstream repository is affected by this change ### Release note ```release-note NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Tests** * Version validation now compares the checked version with the chart’s application version and reports missing or mismatched versions with the chart path. * Monitoring image checks now verify use of the configured mirror registry and require a 64-character lowercase SHA-256 digest, without requiring a specific image name. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
23461e065d |
fix(redis): run one Sentinel when Redis runs a single pod (#5151)
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `kind/backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does The chart pins `spec.sentinel.replicas: 3` whatever `replicas` says, so an instance created with `replicas: 1` runs one Redis pod and three Sentinel pods that have nothing to fail over to. This makes the Sentinel count follow the data pods: one Sentinel when `replicas` is 1, three otherwise. The operator cannot run zero Sentinels (freshworks `Validate()` resets `sentinel.replicas <= 0` to 3), and it needs at least one to locate the master. With one Sentinel its quorum is `replicas/2+1` = 1, so the single Sentinel works on its own. The operator also gives the Sentinel Deployment a PodDisruptionBudget with `minAvailable: 1` whenever Redis has two replicas or fewer (`ensurePodDisruptionBudget` in `operator/redisfailover/service/client.go`). On a single Sentinel that budget refuses every eviction and would block node drains, so the chart sets `sentinel.disablePodDisruptionBudget: true` in that case only. The Sentinel `WorkloadMonitor` now expects the rendered count instead of a fixed 3/2. The operator only stops ensuring that PDB once the flag is set; it never deletes the one it created while three Sentinels ran (`EnsureSentinelDeployment` in freshworks v3.3.5). An existing instance scaled to one replica would keep it, so `templates/hooks/sentinel-pdb.yaml` adds a post-upgrade hook that deletes `rfs-<release>`, with a Role scoped to that one name. It renders on upgrade only, since a fresh one-replica install never gets that PDB. From `replicas: 2` up nothing changes. Scaling an existing instance between 1 and 2 replicas scales the Sentinel Deployment between 1 and 3 accordingly. Not in scope: with `replicas: 1` the Redis data pod keeps its own operator PDB (`minAvailable: 1`), which already blocks drains of its node today; that is unchanged here. Tests: `tests/sentinel_replicas_test.yaml` covers 1, 2, 3 and the default, the PDB switch, and the `WorkloadMonitor`. Three of its cases fail on `main` without the template change. `tests/sentinel_pdb_hook_test.yaml` covers the hook. `helm unittest packages/apps/redis`: 79 passed. Open PR #4990 also edits `redisfailover.yaml` (Sentinel spreading); the two touch different keys. Backport: `release-1.7` ships the same freshworks operator and CRD, so `kind/backport` applies. `release-1.6` is not backportable as is: it still runs spotahome `v1.3.0-rc1`, whose vendored CRD (chart 3.3.0) does not declare `sentinel.disablePodDisruptionBudget`, and the operator's CRDs are installed with Flux's default `crds: Create` policy, so they are never updated on an existing cluster. The API server would prune the field and the single Sentinel would keep a `minAvailable: 1` PDB that blocks node drains. A 1.6 fix would need a CRD update path first. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Downstream repositories <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [x] No downstream repository is affected by this change (walked the map: the diff changes a template, a test, and the wording of two descriptions; no field, default, enum or name changes) - [ ] [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 <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note fix(redis): an instance with `replicas: 1` now runs one Sentinel instead of three, without a Sentinel PodDisruptionBudget; on upgrade, the PDB the operator created earlier is deleted. Instances with two replicas or more are unchanged. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Redis deployments configured with one replica now use one Sentinel; other replica settings continue to use three. * Workload monitoring now reflects the configured Sentinel count. When changing to one replica, upgrades remove the Sentinel PodDisruptionBudget. * **Documentation** * Updated Redis configuration guidance to explain how Sentinel count varies with the replica setting and clarify Sentinel resource defaults. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d61db784a6 |
fix(mariadb-operator): derive the cluster name from the platform domain (#5123)
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `kind/backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does Fixes #2348. `packages/system/mariadb-operator/values.yaml` pins `mariadb-operator.clusterName: cozy.local`, and the vendored chart turns it into the operator's `CLUSTER_NAME` and into a SAN of the webhook certificate. On a platform installed with any other `networking.clusterDomain`, the operator resolves its own pods as `<name>.<ns>.svc.cozy.local` and gets NXDOMAIN. Observed on a cluster with `clusterDomain: lab.local`: a 2-replica MariaDB never gets replication configured (`status.replication` stays `{}`, the secondary runs as a standalone semi-sync master), and User, Database and Grant fail with `lookup mariadb-<name>-primary.<ns>.svc.cozy.local ... no such host`, while the operator only retries at verbosity 1. `clusterName` is a value of the vendored subchart, which the `_cluster` lookup used by package templates cannot reach. The platform now carries `networking.clusterDomain` under the `mariadb-operator` subchart key in the shared `cozystack-values` Secret, the same route `keda.clusterDomain` and `opensearch-operator.manager.dnsBase` already take. The literal in the package stays as the fallback for a render without that Secret, so a cluster on `cozy.local` renders exactly as before. Tests cover both ends of the path: `packages/core/platform/tests/apps_mariadb_operator_cluster_name_test.yaml` pins that the Secret carries the domain under the anchored subchart key and tracks a non-default value (it fails with the `apps.yaml` change reverted), and `packages/system/mariadb-operator/tests/cluster_name_test.yaml` pins that a domain set on that key reaches the operator's `CLUSTER_NAME` and the webhook certificate, with `cozy.local` kept as the standalone default. `make helm-unit-tests` passes locally. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Downstream repositories <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [x] 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 <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note fix(mariadb-operator): the MariaDB operator now uses the platform's networking.clusterDomain instead of a hard-coded cozy.local, so replication and User/Database/Grant reconciliation work on clusters with a non-default cluster domain. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * MariaDB now uses the configured networking domain as its cluster name and webhook certificate address. If no custom domain is configured, it continues to use the default cluster name and address. * **Tests** * Added coverage for default and custom domain settings, including their effect on the MariaDB cluster name and webhook certificate address. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
0c486263f9 |
fix(postgres): render the declared databases in a stable order (#5091)
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `kind/backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does The postgres init script lists the declared databases with `keys .Values.databases`, which returns them in Go's randomized map order. A release with two or more databases therefore renders a different `DEFINED_DBS` line on each upgrade, so the `<release>-init-script` Secret and the `checksum/config` annotation of the init Job change on upgrades that did not touch the databases. Sorting the keys makes the render a function of the values alone. Found while measuring a platform-wide values change on a production cluster (#5092): of 36 releases that ran Jobs again, 31 rendered a byte-identical manifest across the two Helm revisions and the other 5 were Postgres releases whose only difference was this ordering, e.g. `DEFINED_DBS="db_a db_b"` → `DEFINED_DBS="db_b db_a"`. This does not stop the init Job from running on every upgrade (it is a `post-upgrade` hook); the issue covers that part. Verification: the new `tests/init_script_deterministic_test.yaml` renders eight databases declared out of order and expects them sorted. Without the fix it failed 5 runs out of 5 with a different order each time; with it, `helm unittest packages/apps/postgres` passes (27 suites, 210 tests). ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Downstream repositories <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [x] 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 <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note fix(postgres): the init script renders the declared databases in a stable order, so an upgrade that leaves the databases unchanged no longer rewrites the init-script Secret. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * Database initialization now lists configured databases in alphabetical order, providing consistent output across deployments. * **Tests** * Added coverage to verify the alphabetical ordering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
72ff22dc9e |
fix(securitygroups): project a membership-only group to a policy Cilium accepts (#5133)
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `kind/backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does Fixes #5129. A SecurityGroup with `attachments` and no rules still projects to a backing CiliumNetworkPolicy, since that policy carries the attachments the securitygroup-controller reconciles. Its spec was `endpointSelector` plus `ingress: []`. The CRD `anyOf` accepts that, but the agent's `api.Rule.Sanitize` counts entries (`len(r.Ingress) == 0 && ...`, the same in v1.19.5 and v1.20.2), so Cilium marks the policy `Valid=False` with "rule must have at least one of Ingress, IngressDeny, Egress, EgressDeny". `pkg/apis/sdn/DESIGN.md` §5 claimed the opposite. The projection now writes one empty ingress rule, which allows nothing, plus `enableDefaultDeny: {ingress: false, egress: false}`, so that rule does not put the member pods into default deny. The override relies on the agent's `enable-non-default-deny-policies`, which defaults to `true` in the vendored chart; DESIGN.md says so. `ingress` is now `omitempty` like `egress`, because an empty list no longer serves any purpose. The reverse projection hides that exact spec, so the group still reads back with no rules. `enableDefaultDeny` is what tells it apart from a tenant-authored empty ingress rule, which keeps its default deny and stays visible. `TestRulesLessSecurityGroupProjectsValidPolicy` now checks the wire form against the Sanitize rule and fails on `main` (`ingress:[]`). `TestEmptyIngressRuleIsNotMembershipOnly` covers the tenant-authored empty rule. `go test ./pkg/registry/sdn/...` passes. Checked on a lab running Cozystack v1.6.5 with Cilium 1.19.5 (`enable-non-default-deny-policies: true`), in a throw-away namespace with no baseline policy. A membership-only group `front` attached to `web`, and a group `db` on `srv` allowing `fromSG: [front]` on 8080. The v1.6.5 projection gives `front` as `VALID=False`. With the spec this branch writes for `front` applied to its CNP, it turns `VALID=True` ("Policy validation succeeded"), and every probe is unchanged: `web -> srv:8080` OK, `web -> srv:9090` and `other -> srv:8080` refused, `other -> web:8080` and `web -> other:8080` OK, so the placeholder isolates nothing. Moving the attachment of `front` from `web` to `other` flips the `fromSG` peer: `other -> srv:8080` OK, `web -> srv:8080` refused. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Downstream repositories The diff touches `pkg/registry/sdn/securitygroup/` and `pkg/apis/sdn/DESIGN.md` only: no package, chart, CRD, `hack/` or convention change. The website mentions SecurityGroups only in the v1.6 blog post and the PCI-DSS page, neither of which describes the backing policy. <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [x] 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 <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note fix(securitygroups): a SecurityGroup with attachments and no rules no longer leaves an invalid CiliumNetworkPolicy behind; its backing policy is now accepted by Cilium and still allows or denies nothing. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * Membership-only SecurityGroups now round-trip without displaying ingress or egress rules, while remaining usable without triggering default-deny behavior. * SecurityGroups with an explicitly configured empty ingress rule continue to retain that rule when read back. * This behavior depends on Cilium’s `enable-non-default-deny-policies` option being enabled; when disabled, Cilium applies default deny to every policy. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9e8e10c307 |
fix(backups): re-render the default backup objects when the S3 endpoint changes (#5230)
## What this PR does Refs #5205. The S3 endpoint the platform backups use is copied twice, and neither copy is refreshed after `publishing.host` changes. The COSI sidecar writes `BucketInfo` once per `BucketAccess`, so the credentials Secret `bucket-cozy-backups-system-credentials` rendered from it keeps the old endpoint. The Velero BSL and the default Strategy CRs keep whatever endpoint the last render of `backupstrategy-controller` saw. The default objects gate only acted on missing objects, so backups kept going to the old host and nothing reported it. The gate now also forces a render when a copy is stale: - the bucket's `<bucket>-system` release, when the credentials Secret's endpoint differs from the COSI Secret it is rendered from; - `backupstrategy-controller`, when the endpoint an object was rendered with differs from the credentials Secret's. The chart stamps the rendered endpoint on the BSL and on every default Strategy as `backups.cozystack.io/endpoint`, because the endpoint itself sits at a different path in every kind. Objects without the annotation count as stale until a render stamps it; `MinForceInterval` bounds the forces. Stale credentials are forced before the BackupClass and the routed objects are read, so an absent BackupClass or an unreadable Strategy still fails the check but does not keep the old endpoint in the credentials Secret. A `BucketInfo` without an endpoint is not compared, since rendering from it would write an empty endpoint or fail the bucket release. Repairing missing objects keeps working while the credentials comparison is stale or fails, and a `systemSecretName` that is not derived from a COSI Secret (no `-credentials` suffix, or the raw COSI Secret itself) is not compared at all. RBAC is unchanged: the controller already holds cluster-wide `get` on Secrets. No new metric: nothing would consume it. What this does not do: the COSI Secret still holds the endpoint the `BucketAccess` was granted with, so after a host change the endpoint follows only once an operator deletes the `BucketAccess` and forces `bucket-cozy-backups` to recreate it (this issues new S3 keys). From there the gate replaces the remaining manual steps of the recovery described in the issue. Automatic re-grant is left for a separate issue, and the operator runbook goes to the website, hence `Refs` rather than `Fixes`. Known limitation: the credentials comparison runs only when `backupStorage.systemSecretName` is `bucket-<bucketName>-system-credentials`, the Secret the forced `bucket-<bucketName>-system` release renders. If an operator sets `bucketName` and `systemSecretName` inconsistently, the comparison is switched off rather than forcing a release that never rewrites the compared Secret, so a stale endpoint in that Secret is not repaired. Repairing a missing credentials Secret still forces the release derived from `bucketName`, as before this PR. Tested with unit tests for the gate (including the comparison normalisation shared with the chart helpers), helm-unittest for the annotation and the wiring, and a bats guard that every rendered Strategy CR and the BSL carry the annotation. On a live cluster, a server-side render of the chart produced exactly the annotation the gate derives from the real credentials Secret, so the steady state does not force anything. ### Screenshots Not a UI change. ### Downstream repositories - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: an operator runbook for changing `publishing.host` with platform backups enabled (re-grant the `BucketAccess`, then let the gate converge; restart ClickHouse pods that read the credentials from environment variables). Not filed yet, pending approval of the text. - [ ] [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 fix(backups): the backup controller now re-renders the Velero BSL, the default backup strategies and the platform bucket's credentials Secret when their S3 endpoint is stale, so after the platform bucket's BucketAccess is re-granted for a new publishing.host, backups move to the new endpoint without forcing releases by hand. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Backup strategies and storage locations now record their storage endpoint, enabling detection and repair of outdated endpoint settings after bucket access changes. * Bucket credentials and backup objects are refreshed independently when their endpoints are missing or out of date. * **Documentation** * Clarified when endpoint changes trigger updates and how suspended releases are handled. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7097cc5cda |
feat(release): make backports automatic with backport-batch (#5106)
## What this PR does Backports become automatic. Fix merged to `main` with `fix` title, or `security` for fix that comes through security advisory, reaches release lines through batch that runs on Monday and Thursday, newest line first, nobody requests, labels or opens backports by hand. Batch runs for two newest lines with stable release (`released-lines` in policy block) and every line branched after them that has no stable release yet: with 1.6 and 1.7 released, `release-1.8` created at `v1.8.0-rc.1` joins them, and `v1.8.0` drops 1.6. Line counts as released once `vX.Y.Z` tag without pre-release suffix exists on origin. Rules are in `docs/release/backports.md`, `cmd/backport-batch` applies them, `.github/workflows/backport-batch.yaml` runs it. Design is in cozystack/community#114, merge shape of each original in cozystack/community#119. - `backport-batch run --line release-X.Y` takes PRs merged to `main` since line branched (title and body read from merge commit) and skips what rules exclude: other types and `deps`, breaking changes, platform migrations, `targetVersion`, changed CRD documents, API changes that API review gate calls sizeable (batch runs `internal/apigate` itself whenever PR changes a file the gate parses: `pkg/apiserver/apiserver.go`, an ApplicationDefinition, or YAML under `packages/` or `internal/` naming `CustomResourceDefinition` or `APIService`), and on older line what next newer line doesn't carry yet. The rest goes onto `backport-batch/<line>-<date>` in merge order. PR merged to `main` with merge commit goes as its own commits picked with `cherry-pick -x --signoff`, so each keeps its author, message and sign-offs and gets sign-off of whoever runs the batch (with `-m 1` author was whoever merged on `main`), merges of `main` into PR branch are left out, and merge commit joins them to the batch with original's merge message and `Backported-from:` trailer. Squash-merged PR goes as one pick. Conflict on any commit takes branch back to where it was before that original, it's listed with the commit that failed, run goes on. `--push` opens or updates draft PR, `--continue` carries on open batch PR as fast-forward only, PR body it didn't write is never rewritten. Same-day run over batch branch whose PR never got opened refuses and names `run --line <line> --onto <head> --push`, which continues it. Run or `--defer` that resets local batch branch over commits only it has prints `git branch` command that restores them. - `run --defer <N> --reason <why>` takes original out of open batch (usually the one failing CI is pinned on). It rebuilds branch replaying batch unit by unit without it, so merges and pushed conflict resolutions keep their shape, and only this rebuild pushes with `--force-with-lease`. Deferred original comes back in next batch. - Line carries something only if its own history shows it: picks whose `-x` line, the one git appends, names PR's merge commit or every commit of its own, or pick that changes nothing. Picked commits keep their messages and batch merge carries PR body, so text in them (`-x` lines, `Backported-from:`, `Backport of #N`) counts only on commits from before `batch-only` day in policy block, where it records old hand backports and batches. Otherwise text in a merged fix could mark another fix as already backported, and it would never reach the line. PR against line never counts. Open batch PR counts only from this repo and from author with write access or the batch's own bot (`BACKPORT_BATCH_BOT`, exact login, `cozystack-ci[bot]` when unset, so maintainer's run by hand continues scheduled batch instead of opening second one), and is read the same way. Actions bot isn't trusted anymore, any workflow token writes as it. - Any failed GitHub API or git call stops the run with message naming the call, no retries. Failed permission lookup used to count batch PR out as look-alike, so `--continue` opened second batch PR and `notify` posted second comment; now `run`, `check`, `verify` and `notify` exit 2 and write nothing. - Policy applies fully from `release-1.7`. `release-1.6` takes only fixes merged from 2026-10-26, older ones need `backport/include`. Start days are in `backport-policy` block on the policy page, test keeps page and code in sync. - `backport-batch check --cut` replaces `backport-audit` as gate before patch cut and before every rc after the first, it also fails while a fix is only on open batch PR. Plain `check`, which scheduled run uses, lets those through. `backport-batch fold` checks conflict resolution in the same shape: merge on batch head whose second parent is straight line of picks of PR's own commits in order, each with its `-x`, only PR's files (one pick for squash). `backport-batch verify` holds every original on batch to that shape, clean picks included, and `pr-labeler.yaml` runs it on every push to batch PR: `pull_request_target` so it reaches `release-*`, tool built from `main`, batch head fetched as data, read-only token. Batch PR body says per original how many commits it brought and which picks differ from their commits. `backport-batch notify` reports batch that is still open from previous run and deferred conflicts in one comment on batch PR, edited in place, with `BACKPORT_ALERT_TEAM` mention. - `hack/backport-batch-scheduled.sh` is what the workflow runs and maintainer can run by hand (`--dry-run` changes nothing on GitHub): for each line newest first `run --continue --push`, `check`, `notify`. It pushes with `BACKPORT_BATCH_TOKEN` only and fails without it, never `GITHUB_TOKEN`, because its pushes don't start CI. Workflow runs it as cozystack-ci App: it mints token for this repo only with Contents and Pull requests write, passes it as `BACKPORT_BATCH_TOKEN`, commits as `cozystack-ci[bot]`, and sets `BACKPORT_BATCH_BOT` so the tool trusts that bot's batch PR and comment. Job timeout is 30 minutes, under the token's hour. - App has no workflows permission, so workflow runs the script with `--no-workflow-files`: original that changes `.github/workflows/` isn't picked and waits on batch PR for maintainer, listed with its workflow files in body and in notify comment. It's never a conflict. `check` warns about it while it waits and `check --cut` fails on it. Maintainer brings it over with own token by `run --continue --push` without the flag, or takes it out with `--defer`. - Deferred conflicts and failed batch CI wait for maintainer. `notify` keeps one comment on batch PR listing stalled batch, conflicts and workflow changes, mentioning `BACKPORT_ALERT_TEAM` (team or login) when it's set. Failed batch CI isn't in the comment, it shows on batch PR checks, and `check --cut` fails while batch is unmerged. - Two steps that need judgement (resolve conflict, pin failed CI on original and defer it) are written as runbooks in `docs/release/backport-tasks/`: inputs, the only allowed output, check it must pass before push, what never to do. - `backport/include` and `backport/skip` let maintainer override rules for single PR, they are new `backport/` category in `labels.yml`. Skip wins over include, if open batch already picked it `--defer` takes it out. - Text written by other people (titles, release notes, reasons, file names) goes to batch PR body only inside code spans or release-note block, control, bidi and zero-width characters are dropped, file names in deferred block included, git args go after `--end-of-options`, no shell. - Removed: `cmd/backport-audit`, `backport.yaml`, `kind/backport` and `kind/backport-previous` labels with aliases and stale exemption, trailer-check exemption for bot branches. `Commit trailers` job exempts only `backport-batch/*` branches of this repo, changelog instructions read originals from batch commits. Exemption lives in main's `pre-commit.yml`, and batch PR runs its line's copy: `release-1.7` has its own copy that exempts only old `backport-<pr>-to-<branch>` branches, `release-1.6` has no such job. So on `release-1.7` picked commit with bad trailer fails batch CI until line gets main's copy. - `hack/close-manual-backport.sh` runs from `pr-labeler.yaml` on `opened` and closes PR against `release-*` from outside contributor (association contributor, first-time contributor or none, permission read or none). Owners, members, collaborators, anyone with triage or more and bots are left alone. When GitHub's answer on author's permission can't be read, it closes nothing and fails the job naming the failed read. Maintainer keeps release-only fix by reopening it. Release-only PRs are occasional and opened by maintainers, for fix with no counterpart on `main`, or one that on `main` came inside another PR or was written for `main`; if its counterpart on `main` is a fix the batch would take, the batch skips it once picking it changes nothing on the line, otherwise maintainer labels it `backport/skip`. - `docs/release.md` is split into pages under `docs/release/`, PR template doesn't ask for `kind/backport` anymore. `security` type is added to commit format in `docs/agents/contributing.md`, PR template, Gemini review guide and labeler's list (labeler maps no `kind/*` to it, `labels.yml` has no fitting one). - `SECURITY.md`, `docs/security/incident-response.md` and `GENERAL_TECHNICAL_REVIEW.md` now say two latest minor releases are supported, same window the batch runs for, and link the backport policy for which fixes go there. Dry runs without push, on 2026-11-06, with `--no-workflow-files` as the workflow runs it: `lines` gives `release-1.7` and `release-1.6`. On `release-1.7` batch picks 12 fixes, defers 3 conflicts (#4756, #4867, #5082), leaves #4888 and #4971 to a maintainer because they change workflows, and skips 49. On `release-1.6` it picks #5088, defers 3 conflicts (#5071, #5086, #5087), leaves #5142 to a maintainer, and skips 590. `verify` passes both batches. Setup after merge: no new secret. Batch runs as cozystack-ci App from existing `COZYSTACK_CI_APP_ID` and `COZYSTACK_CI_PRIVATE_KEY`, job fails when either is missing. App's installation already has Contents and Pull requests write on all org repos, and no Workflows is needed: PRs that change `.github/workflows/` wait for maintainer, who brings them over with own token as "Changes to workflows" in `docs/release/backports.md` says. Today that's #4888 and #4971 on `release-1.7` and #5142 on `release-1.6`. Set `BACKPORT_ALERT_TEAM` to a team or a login (for example `cozystack/maintainers`): unset, batch comment mentions nobody and nobody is notified. Once this PR is merged, `Backport batch shape` can be required check in the release lines ruleset (`release-*` excluding `release-*.*.*`), with "Do not require status checks on creation" on, because `cut-prerelease.yaml` creates `release-X.Y` by push. `PR Auto-Label` with both new jobs runs on `pull_request_target`, so it needs #5135 sorted before 2026-12-02. `kind/backport*` labels have to be deleted by hand, label sync doesn't delete. ### Screenshots Not a UI change. ### Downstream repositories cozystack/community `contributors/README.md` lists commit types, so it needs `security` too, it has no backport label text. Website has no backport label instructions, and its links to `docs/release.md` carry no anchor, the page stays as index. Trigger map lists commit convention change for cozystack/ccp: this adds `security` type, but not message format or trailer, and `package-bump` writes only `chore(...)` commits and restates the trailer, so nothing changes there. - [ ] 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: add `security` to commit types in `contributors/README.md` ### Release note ```release-note feat(release): backports are automatic. Fixes merged to main with fix or security title reach release lines through a batch built by cmd/backport-batch every Monday and Thursday, by the rules in docs/release/backports.md. The batch runs for the two newest lines with a stable release and for a line in its rc window: the release-X.Y branch the first rc creates joins at once, and its vX.Y.0 drops the oldest line. Features, chores, docs, dependency bumps, breaking changes, migrations, CRDs and API changes that the API review gate calls sizeable wait for the next minor. The policy applies fully from release-1.7, release-1.6 takes only fixes merged from 2026-10-26. Each original reaches the line as its own commits under a merge commit, keeping their authors and sign-offs. Maintainers override the rules for single PR with backport/include and backport/skip labels and take an original out of a batch with backport-batch run --defer. backport-batch check replaces backport-audit as the gate before a patch release and before a later rc. The label-driven backport workflow and kind/backport labels are removed. Backport PR opened by hand against a release branch by author with read access or less is closed automatically. The scheduled batch runs as the cozystack-ci App and needs no secret of its own; a fix that changes .github/workflows waits on the batch pull request for a maintainer, since the App may not push workflow changes. Release documentation now lives in one page per topic under docs/release/. ``` |
||
|
|
9fba8af69c |
test(redis): pin the release name in the Sentinel PDB hook for the longest release
The long-name case checked only that the Job name fits in 63 characters and that the Job targets the right PDB. A Job name that dropped the release, letting two releases' hooks replace each other, or a Role granting a different PDB would both have passed. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io> |
||
|
|
e92bc8664b |
fix(tests): keep two unit tests green after the release image restamp
The release build rewrites packages/*/values.yaml: the kube-ovn image tag becomes the Cozystack version and the traces collector becomes the first-party otelcol-tracing build. Two unit tests read exactly those fields, so every PR against release-1.7 has failed Unit & controller tests since the first rc, and release-1.8 would repeat it. Same change as #5179 on release-1.7. Assisted-by: LLM Signed-off-by: Marian Koreniuk <moriarti@cp.if.ua> |
||
|
|
a7f84242d1 |
fix(redis): keep three Sentinels for a zero or negative replica count
The operator runs three Redis pods when the count is zero or negative, but the chart treated any count up to 1 as the single-pod case. Such a cluster got one Sentinel and no Sentinel PDB, while three Redis pods ran under it. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io> |
||
|
|
fd9245f008 |
test(lineage): reject CAPK labels on objects that are not KubeVirt VMs
The bridge from a VirtualMachine to its KubevirtMachine is guarded by the object's API version and kind, but no test exercised that guard: weakening it to match on either field alone still passed, and let a labelled kubevirt.io Cluster or a VirtualMachine of another group resolve to the Kubernetes application. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io> |
||
|
|
c6c107fd08 |
docs(backups): state when the gate compares endpoints in the values reference
The values reference read as if the credentials endpoint were always compared and an object without the endpoint annotation never forced a render. Operators sizing the force counter or choosing a custom systemSecretName need both conditions spelled out. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
ee5e51ee6c |
fix(backups): keep forcing objects that still lack the endpoint annotation
An object without backups.cozystack.io/endpoint stopped counting as stale as soon as one forced render had been requested, whether or not that render succeeded. A failed upgrade left the objects unannotated and the gate silent until the controller restarted, which also hid the climbing force counter meant to report exactly that failure. Such an object now stays stale until a render stamps it; MinForceInterval already bounds the cost. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
dc909c778c |
fix(backups): compare the credentials endpoint only for the forced bucket's Secret
The gate forces bucket-<bucketName>-system, which renders the credentials Secret of that bucket's system user and nothing else. The COSI Secret name was derived from systemSecretName instead, so a value naming another bucket's Secret made a lagging endpoint there force the platform bucket release every throttle window without ever refreshing the Secret that was compared. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
bd2b6c4b49 |
fix(backups): leave the credentials Secret alone when BucketInfo has no endpoint
A BucketInfo without an endpoint differs from every credentials Secret that has one, so the gate forced the bucket release on every throttle window. That render cannot converge: an empty endpoint would be written into the Secret every driver reads, and with the key absent the template's trimPrefix fails and parks the release in Failed. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
399ccd3f4a |
fix(backups): refresh stale credentials even when the routed objects are unreadable
The credentials endpoint comparison needs only the credentials Secret and the COSI Secret it is rendered from, yet its forced render was issued only after the BackupClass and every routed object had been read. A missing BackupClass or one unreadable Strategy therefore kept the old endpoint in the credentials Secret, which the projector hands to every driver, not just the one whose object failed. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
ef7626a951 |
fix(backups): keep repairing missing default objects while credentials lag
The credentials comparison returned early when the credentials Secret was stale or the COSI Secret could not be read, so a missing default Strategy or BSL was no longer created on that tick, nor on any later one while the bucket release stayed suspended, kept failing, or the COSI Secret stayed unreadable. Repairing missing objects is what the gate did before the comparison existed, and it does not depend on the answer. Run the object check on every tick and force each release for what it renders, reporting a comparison error alongside instead of in place of the repair. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
d0f47f7dd5 |
fix(backups): compare the credentials endpoint only for a Secret derived from COSI
backupStorage.systemSecretName may name a Secret the bucket chart does not render from a COSI Secret: the raw COSI Secret itself, which the projector accepts, or any Secret without the -credentials suffix. The COSI Secret name was then the source name unchanged, so the gate compared a Secret with itself and either reported it stale on every tick or failed the check, and stopped repairing missing default objects as well. Skip the comparison when there is no distinct COSI Secret or no endpoint key to compare. The gate compares the annotation with what the chart's endpoint helper renders, and the helper renders a present but blank endpoint as a bare https:// instead of falling back, so the gate has to compute the same value or its expectation and the chart disagree for that Secret. The gate's docs, metric help and log lines named only missing objects, so they did not cover a stale endpoint they now report. Assisted-by: LLM Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io> |
||
|
|
cb3fcd3357 |
feat(operator): let the platform source trust a private registry CA (#5237)
## What this PR does The operator creates the `cozystack-platform` OCIRepository from its own command-line arguments and rewrites the whole object on every start; `cozystack-packages` copies that spec. When the platform source is a registry whose certificate is issued by a private CA (an internal pull-through cache in an air-gapped install, for example), there is no durable way to make Flux trust it: a `certSecretRef` patched onto the live object is dropped on the next operator restart, source-controller then fails TLS, and platform updates silently stop arriving. This adds `--platform-source-cert-secret` to the operator and `cozystackOperator.platformSourceCertSecret` to the installer chart. The named Secret in `cozy-system` is set as `spec.certSecretRef` on the generated OCIRepository, so it holds `ca.crt` for a private CA and/or `tls.crt`+`tls.key` for client certificate auth, with the Flux key layout. `cozystack-packages` inherits it through the existing spec copy. The flag is OCI only. `GitRepository` has no `certSecretRef` (Flux reads a Git CA from the `ca.crt` key of the `secretRef` Secret), so a Git platform source with the flag set is refused at startup instead of having its CA silently ignored. Tests: unit tests for the generated spec, a regression test that the CA survives the operator's update of an existing OCIRepository, a test that a Git source with the flag writes nothing, and a helm-unittest suite for the new chart value. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Downstream repositories <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [ ] No downstream repository is affected by this change - [x] [cozystack/website](https://github.com/cozystack/website) - follow-up: https://github.com/cozystack/website/pull/791 - [ ] [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: The new installer value is optional, so `ansible-cozystack` is not reached. The website's air-gapped guide (`content/en/docs/next/install/kubernetes/air-gapped.md`) added the private CA to the nodes only, while Flux fetches the platform source itself; cozystack/website#791 adds a step for `platformSourceUrl`, `platformSourceSecret` and `platformSourceCertSecret`. ### Release note ```release-note feat(operator): the platform source can be served from a registry behind a private CA. Set `cozystackOperator.platformSourceCertSecret` in the installer to a Secret in `cozy-system` with `ca.crt` (and optionally `tls.crt`/`tls.key`); the operator sets it as `certSecretRef` on the `cozystack-platform` OCIRepository, so it survives operator restarts. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Added the `platformSourceCertSecret` setting for OCI platform sources. It allows a Secret containing certificates for private CA trust or client authentication to be configured separately from registry credentials. * The configured certificate Secret is applied to the OCI source and inherited by its package source. * **Configuration** * The setting is empty by default. Git platform sources do not support a separate certificate Secret; use the existing platform source Secret for Git TLS certificates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
41507e36aa |
docs(kubernetes): say where worker registry mirrors are configured
The air-gapped note said in-guest image pulls honour no per-tenant mirror until a Phase 2 that has landed, so an operator setting up an air-gapped cluster would read that mirroring is unavailable and file an issue. It now says the Secret and the platform registries value are removed, that a Package still setting the value keeps installing, and that worker mirrors are set per pool through talos.registryMirrors of the KubernetesNodes resource. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
0a448963e3 |
docs(installer): say a CA-only cert secret must be Opaque
kubernetes.io/tls requires both tls.crt and tls.key, so a Secret that carries only ca.crt is rejected under that type. Assisted-by: LLM Signed-off-by: Kirill Ilin <stitch14@yandex.ru> |
||
|
|
fbfc270c71 |
test(installer): tie each platform source flag to its own value
Both secrets were only ever set together, so a cert flag keyed on platformSourceSecret stayed green. A pull-through cache behind a private CA usually needs the CA without credentials, which is exactly the case such a slip breaks. The absence checks match the flag by prefix, since a whole-element comparison misses the flag rendered with any value. Assisted-by: LLM Signed-off-by: Kirill Ilin <stitch14@yandex.ru> |
||
|
|
dfd7cbed36 |
fix(operator): reject a Git cert secret before installing CRDs and Flux
CRDs and Flux are installed before the platform source, so refusing a cert secret on a Git source only when the source is written exits the operator after a partial install. The check now runs right after flag parsing, and the source install reuses it. The guard runs on every start, so a Git source without a cert secret is now covered over https and ssh: a guard that misfired there would crash-loop the operator for every Git platform source. The update test also renames the cert Secret, as a CA rotation does. Assisted-by: LLM Signed-off-by: Kirill Ilin <stitch14@yandex.ru> |
||
|
|
1ceaeb8b6e |
docs(security): support the two latest minor releases
The security policy promised three supported lines and said no rule decides which of them a fix reaches. The backport batch runs for the two newest lines with a stable release, by the rules on the backport policy page, and the maintainers agreed to make that the support window. SECURITY.md, the incident-response summary and the technical review now name the two latest minor releases, say that the older one drops out when a new minor ships, and send a reader to the backport policy for which fixes reach them. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
eaa0169ff3 |
chore(release): retire the backport bot, its labels and backport-audit
The batch replaces the label-driven backport workflow and the audit that gated a cut against it. The kind/backport labels, the stale exemption for them, their contract tests and the audit's make target go with them. labels.yml gains a backport/ category instead, with backport/include and backport/skip, the overrides the batch reads off a merged pull request. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
4a750a1e23 |
ci(release): run the backport batch twice a week
The batch runs on Monday and Thursday from a script a maintainer can also run by hand. It runs as the cozystack-ci App with a token for this repository alone and Contents and Pull requests write, so its pushes start CI on the batch and no person's token is involved. The App may not push a workflow change, so such an original waits for a maintainer. pr-labeler holds every batch pull request to the batch's shape, with the tool built from main and the head read as data. It also closes a pull request into a release line opened by someone with read access or none, since fixes reach the lines through the batch. The Commit trailers check exempts a batch branch of this repository, whose picked messages cannot be rewritten. Promotion now says how to bring its own pipeline to a line through the batch. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
9f8c88b4f1 |
feat(release): build the backport batch with backport-batch
The rules need a tool that applies them the same way on every run and shows what it did. backport-batch reads main's first-parent history, judges each pull request by the policy block it reads from the page on main, and picks what qualifies onto one batch branch per line. Each original lands as its own commits under a merge that names it, so authors and sign-offs survive and the line's own history shows what it carries. check is the gate before a cut. fold holds a hand resolution of a conflict to the batch's shape, verify holds a whole batch to it, and notify reports what waits on the batch for a maintainer. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
c12dcdddb4 |
docs(release): make backports automatic, by written rules
Backports were asked for pull request by pull request with labels and opened by a bot, so whether a fix reached a release line depended on someone remembering to ask, and the lines drifted apart. The policy page states the rules instead: a fix or security fix merged into main goes to every line the batch runs for, unless it is breaking, touches platform migrations or a CRD, or changes the API in a way the API review gate calls sizeable. Two labels let a maintainer override the rules for one pull request. The task pages give a maintainer the steps for a deferred conflict and for a failed batch CI. The backports route in AGENTS.md, the contributing and changelog guides and the PR template point at the policy, and the commit format lists the security type the policy takes. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
970191dc8a |
fix(clickhouse): restore the archive a Backup was taken as (#5208)
## What this PR does A ClickHouse `RestoreJob` restored the newest archive under the app prefix, whichever `Backup` it named. Strategy pod generated the archive name with `date -u` and nothing was recorded on the `Backup`, so restore could only list the remote archives and take the last one. Restoring an older `Backup` brought back the latest data. Now the controller names the archive before it creates the backup Job (`clickhouse-<app>-<startedAt>` from persisted `status.startedAt`, so retried pods write the same one), passes it to the pod as `.ArchiveName` and records it in the `Backup` `driverMetadata`. Restore takes exactly that archive and fails with its name when storage has no such archive. The name is recorded only when the Job's pod got it, so a Job left from the previous controller during an upgrade, or a custom strategy with its own names, leaves no name instead of a wrong one. A retried pod reuses the name, so it waits for an attempt the sidecar still runs (no 423) and asks for `resume`. A `Backup` outlives its `BackupJob` and a Plan names `BackupJob`s after the schedule slot, so a `BackupJob` created again under a used name met the earlier run's `Backup` when its Job completed, took it as its result and left the archive it just wrote recorded nowhere. Now it fails if that `Backup` records another archive, and the message names both. A retry of the same run still gets its own `Backup` back. Old `Backup`s have no recorded name and stay restorable: the pod logs a `WARNING` and takes the newest archive stamped no later than `takenAt`, and fails when nothing qualifies. While the chart rolls out, previous controller pod can still render the new template and pass no `takenAt`, so a restore of a `Backup` without recorded name fails before it touches any table, and works when created again after the rollout. Fixes https://github.com/cozystack/cozystack/issues/5199 Tests are go unit tests for naming, recording, a reused `BackupJob` name and restore render, and `hack/altinity-backup-strategy.bats`, which runs the shipped script against a stubbed clickhouse-backup API (the restore tests go red on the old template). Checked on a live ClickHouse with two backups of 1500 and 2000 rows: restore of the older one by recorded name ends with 1500 rows, fallback for an unnamed older backup picks the same archive with the warning, a missing archive fails without touching the table, a retry over an existing local backup completes under the same name. The controller binary itself was not run on the cluster, only its rendered output in the pod. ### Downstream repositories - [x] 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 fix(clickhouse): a RestoreJob now restores the archive its Backup was taken as, not the newest one. New Backups record the archive name, Backups taken before this release are restored best effort from the newest archive stamped no later than their takenAt, with a warning in the restore pod log. A ClickHouse BackupJob created again under a name whose Backup still exists now fails instead of reporting that Backup as its result. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Backups now use a consistent archive name tied to the backup job, and completed backups retain that name for future restores. * Restores can select the recorded archive and backup timestamp. If no archive was recorded, the system selects the newest eligible archive for the application. * **Bug Fixes** * A restore now fails when its recorded archive is missing instead of silently selecting a different one. * Backup requests avoid starting a duplicate action when the matching action is already in progress. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
18779dddfc |
fix(backups): report sizeBytes for MariaDB and CNPG backup artifacts (#5223)
## What this PR does Fixes https://github.com/cozystack/cozystack/issues/4316. Continues https://github.com/cozystack/cozystack/pull/5015 by androndo on a separate branch. All ten of their commits are kept as they were (authorship, messages and order, only rebased on current `main`). MariaDB and CNPG backups stored in S3 report `Backup.status.artifact.sizeBytes`, the size of the dump mariadb-operator uploaded or the total of the barman base backup, and the field stays unset when the size cannot be attributed to that one backup. Four commits are added on top: - `test(backups)`: operator Backup fixtures carry the condition mariadb-operator writes on success, `Complete=True` with reason `JobComplete` and message `Success`. Operator sets `Complete=True` for a failed Job too (reason `JobFailed`), so a fixture without reason describes a state that never happens. https://github.com/cozystack/cozystack/pull/5207 makes the driver check that reason, the tests pass with and without it. - `refactor(backups)`: the MariaDB matcher returns the matched dump (key, file name, timestamp from the name, size, LastModified) and tells "not found" from "ambiguous", and the Secret lookup and S3 client live on a small value any reconciler can build from its own client. A MariaDB restore needs this to restore the dump of the Backup it was given and not the newest one under the prefix. `sizeBytes` does not change here. - `fix(backups)`: the probe reads bucket, prefix, endpoint, credentials and TLS from the operator Backup's own `spec.storage.s3`, not from the strategy as it renders now, so editing the strategy during a backup does not send the probe somewhere else. - `fix(backups)`: a dump is attributed only when its ownership is proven. Before, a backup pod with a clock more than a minute behind, right after a previous run, got that run's dump size (666 bytes for a 123-byte dump in a test). Now the dump has to be the only one that either clock places in the operator Backup's lifetime, both clocks have to agree, and neither may place it in the lifetime of another finished operator Backup with the same bucket and prefix. Otherwise the size stays unset. Two cases are still not covered. First, this run's own dump is missing from the listing while the operator Backup of a neighbour was deleted or has not finished, then the neighbour's dump can be taken. Second, backup pod clock and object store clock are both off the same way by more than the minute of slack, which moves a finished neighbour's dump into this run's window, and nothing the clocks record tells them apart. A run whose Job failed has no dump of its own and reaches the probe only because the driver takes `Complete=True` for success, https://github.com/cozystack/cozystack/pull/5207 fixes that. Of the open threads on the original PR, the cache reads, the dump file name layout and the default AWS endpoint test were already handled by its later commits, the clock skew one is the last commit here and the failed Job case is https://github.com/cozystack/cozystack/pull/5207. `minio-go` is new to this tree, this PR adds it as a direct dependency and `internal/backupcontroller/artifact_size.go` is its only importer. The open Bucket strategy PR https://github.com/cozystack/cozystack/pull/4235 picks the same client at the same version. Each clock skew case, and a run whose own dump is gone while the neighbour's operator Backup still exists and has finished, have a unit test that fails on the old matcher, and every new guard has a mutant that turns a test red. I also ran the matcher against a real bucket after four MariaDB backups in a row. Runs that finished more than a minute apart mapped to the dump their operator Job logged, the two back to back came back unset instead of taking each other's dump, and with a run's own dump hidden from the listing the matcher refused the neighbour's dump that the old one returned. ### Screenshots N/A, no UI changes. ### Downstream repositories The only API change is the description of the existing `Backup.status.artifact.sizeBytes` field, no new field, value, default or enum, no package, values or tooling change, and neither cozystack/website nor cozystack/terraform-provider-cozystack references `sizeBytes`. - [x] 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 fix(backups): MariaDB and CNPG (Postgres) backups stored in S3 now report `status.artifact.sizeBytes`: the size of the dump mariadb-operator uploaded, or the total size of the barman base backup (WAL archive excluded). A MariaDB backup takes the size of the one dump that falls inside its run on both the backup pod's and the object store's clock and inside no other finished run, and the size is left unset rather than set to 0 whenever it cannot be measured or attributed. Velero-based backups intentionally keep it unset. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Backup artifacts now report the stored size attributable to an individual MariaDB or CNPG backup when it can be determined. Shared or ambiguous storage may leave the size unset. * **Bug Fixes** * Improved handling of backup artifact creation and lookup errors, including retrying when an existing backup cannot be read and failing jobs when artifact creation is rejected. * **Documentation** * Clarified that an unset size means unknown; zero does not indicate an unknown size. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7f1227e3bd |
feat(installer): expose platformSourceCertSecret
Installing from a registry behind a private CA needs the operator's new --platform-source-cert-secret flag; without a chart value it can only be reached by editing the Deployment, which the next helm upgrade reverts. Assisted-by: LLM Signed-off-by: Kirill Ilin <stitch14@yandex.ru> |
||
|
|
c15a0d01b8 |
feat(operator): let the platform source trust a private registry CA
The operator generates the cozystack-platform OCIRepository from its own arguments and rewrites it on every start, and cozystack-packages copies that spec. A registry behind a private CA therefore had no durable way to be trusted: a certSecretRef patched onto the live object is dropped on the next operator restart, after which source-controller fails TLS and platform updates stop arriving. --platform-source-cert-secret sets spec.certSecretRef from the operator itself, so the CA (or a client certificate) survives the rewrite. Git sources are refused instead of ignored, because GitRepository has no certSecretRef and Flux reads a Git CA from the source secret instead. Assisted-by: LLM Signed-off-by: Kirill Ilin <stitch14@yandex.ru> |