mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-03 18:47:04 +09:00
docs/roadmap-dco
4502
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d76bd1f263 |
fix(mariadb): mount the chart's my.cnf under a .cnf name
mariadb-operator up to v0.0.27 mounted the referenced ConfigMap key as
/etc/mysql/conf.d/my.cnf whatever the key was called. Since v0.0.28 the
file takes the key's own name, so the chart's key "config" became
/etc/mysql/conf.d/config, and the server's !includedir reads only *.cnf
files from that directory. Every setting in the chart's my.cnf has been
ignored since that operator bump: instances run on stock defaults,
including a 128M InnoDB buffer pool inside the 128Mi default limit.
Renaming the key restores the file as intended. Existing instances roll
their pods once, since the projected volume in the pod template changes,
and come back with the chart's settings in effect, including sql-mode
and innodb_flush_log_at_trx_commit, not only the resource sizing.
Fixes:
|
||
|
|
f61e13fdef |
fix(cilium): size the agent memory limit for its BPF maps (#4803)
## What this PR does This raises the cilium-agent memory limit from 4Gi to 24Gi. The 4Gi limit from #3495 has no room for the agent's BPF maps. On nodes with roughly a terabyte of RAM or more, the agent is OOM-killed on every start after a reboot, and the node loses pod networking. The maps grow with node RAM. Cilium sizes its CT, NAT, neighbor and socket rev-NAT maps as a share of total RAM (`bpf-map-dynamic-size-ratio`, 0.0025 by default), and caps each map at 16Mi entries. These are LRU hash maps, so the kernel preallocates them in full. Since Linux 5.11 that memory is charged to the memcg of the process that creates the maps, which is the agent container. This is also why the upgrade itself works. A map reused from bpffs stays charged to the cgroup that created it. The failure shows up on the first reboot after the upgrade, when the maps are recreated inside the limited cgroup. Policy maps don't belong on that list. The issue and the upstream chart's configmap comment both say the ratio sizes them, but at this version they are LPM tries sized by `bpf.policyMapMax`, and they allocate per entry. One DaemonSet serves every node, so the limit can't follow node RAM. The map sizes are capped, though, so a fixed limit can cover any node. With every map at 16Mi entries for both IPv4 and IPv6, the maps take just under 20Gi of kernel memory. Add the 4Gi the agent already had and you get 24Gi. The request stays at 256Mi. The limit doesn't reserve memory, and the pod cgroup still gets `memory.max`, which is what #3495 was after. What it costs is the leak backstop. The agent is now stopped at 24Gi minus its own map memory, so on most nodes a leak in its heap is no longer caught before the node feels it. If someone prefers a tighter backstop over IPv6 headroom, they can lower `cilium.resources.limits.memory` in the networking component values. About 13Gi still covers the IPv4-only map ceiling. I didn't pin the map sizes (`bpf.ctTcpMax`, `bpf.natMax` and so on). No single value works for every node. A value small enough for a 16Gi node cuts the conntrack capacity of a 1.5TiB node many times over. A pinned size also differs from what almost every node above ~14Gi of RAM runs today, so the agent would recreate those maps on upgrade and drop their conntrack and NAT state. A lower ratio has the same problem and doesn't lower the ceiling. The new test keeps the ratio at its default and the sizes unpinned, so changing either has to be deliberate. The figures are computed, not measured. I read the sizing code at cilium 1.19.5 (`pkg/option/config.go`), and 1.20.2, which #3343 moves to, has the same code. Memory comes from that formula plus the kernel hash map layout: a 48-byte element header, the key and value rounded up to 8 bytes, and a 16-byte bucket per power of two of `max_entries`. By that math the IPv4 maps alone pass 4Gi at roughly a terabyte of RAM. Bucket rounding moves the exact point, so I wouldn't read more precision into it. For the 1.5TiB node in the report, the issue's 3.9GiB is the agent's own budget (0.25% of RAM), and the kernel's actual charge comes out higher. Follow-ups, not in this PR: - Operators who want smaller maps on large nodes can size them per node with a `CiliumNodeConfig` selected by node label. This needs a documentation note. - The reporter's `bpftool` measurement of the real map memory is still pending. That is the measured check for the numbers above. The cilium helm-unittest suite passes on this branch, and also with this change applied over the 1.20.2 chart from #3343. Each new assertion fails when its value changes: the limit, the CT, NAT and neighbor sizes, the socket rev-NAT size set through `extraConfig`, and the ratio. Closes #4800 ### Screenshots Not a UI change. ### 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: The diff changes only `packages/system/cilium/values.yaml` and its test. None of the trigger-map entries apply: no app, schema, platform value, variant, release asset or tooling changes, and no website page documents the agent limit. ### Release note ```release-note fix(cilium): raise the cilium-agent memory limit from 4Gi to 24Gi so it covers the agent's BPF maps, which cilium sizes from node RAM. On nodes with around a terabyte of RAM or more, the 4Gi limit got the agent OOM-killed on every start after a reboot, leaving the node without pod networking. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Increased the Cilium agent memory limit to 24 GiB. * Updated validation for BPF map sizing: checks the dynamic size ratio and confirms that specific map-size settings are absent. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
aa804aaa83 |
fix(dashboard): allow editing free-form objects in application forms (#4682)
## What this PR does Free-form object fields, such as NATS `config.merge` and `config.resolver`, get a JSON editor. The form keeps nested objects, arrays and primitive values. Input that is not a JSON object stays visible, and schema validation blocks the create or update request until it is fixed. A JSON object that fails the schema marks the field invalid and points it at the error list. Typed maps keep their key and value editor, and read-only and immutable fields stay protected. Fixes #4676. ### Validation - All 463 console tests in 63 files pass, and so does the TypeScript check. - Tests cover typing into the editor, nested NATS objects next to the typed users editor, input that is not a JSON object, schema errors on a valid object, existing values, immutable fields and free-form objects inside arrays. On the order page they cover a create after a form and YAML round trip, saving an edited object, and blocking input that is not JSON in create and edit mode. - The two schema-error tests fail on the component without the change. ### Screenshots   ### Downstream repositories This change preserves the public resource schema and client contract. The file-level trigger map was reviewed; no companion repository update is required. - [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(dashboard): allow editing free-form objects in application forms ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a JSON editor for configuration fields that accept objects with arbitrary properties. Edit nested values directly, with changes retained when switching between form and YAML views. * **Bug Fixes** * Invalid JSON is flagged and prevents create or update submissions until corrected. The editor supports empty objects, read-only fields, and objects nested in arrays, and makes validation errors accessible. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
6f49e5f427 |
feat(openbao): optional static-key auto-unseal and apiserver egress label (#4766)
## What this PR does This continues #4168 by @txmazing, which has been idle since 2026-09-10. The first commit is the work from #4168 squashed into one, with @txmazing as author. The other two commits constrain the new values and complete the documentation. Five changes to the managed OpenBAO app (`packages/apps/openbao`). 1. **Optional static-key auto-unseal** (#2787, #2483). `seal.type` is `shamir`, the unchanged default, or `static`. With `static` the chart mounts the Secret named in `seal.secretName` through the upstream chart's `server.extraVolumes` and adds OpenBAO's `seal "static"` stanza to the standalone and the Raft config, so the pods unseal themselves after a restart. `seal.previousSecretName` and `seal.previousKeyId` carry the n-1 key during a rotation. The chart never generates the key. A cluster administrator creates the Secret in the tenant namespace, because the `cozy:tenant:*` roles grant nothing on Secrets. A way for tenants to supply secrets by reference is proposed separately in cozystack/community#82. 2. **No silent seal change.** The template reads the seal in use from the `<release>-config` ConfigMap and refuses a different `seal.type` unless `seal.allowMigration: true` acknowledges a migration. Going from `static` back to `shamir` is refused, because the chart cannot render the disabled stanza that direction needs. 3. **Apiserver egress label** (#2793). The server pods carry `policy.cozystack.io/allow-to-apiserver: "true"`, so `service_registration "kubernetes"` reaches the API server and HA pods no longer hang at start. 4. **Constrained values.** The upstream chart passes the server config through `tpl`, and its start script replaces upper case placeholders such as `HOSTNAME` in it. A key identifier with template delimiters would run as a template inside helm-controller, which runs as cluster-admin, so `seal.keyId` and `seal.previousKeyId` take lowercase alphanumerics, hyphens, dots and underscores. The Secret names become pod volume names `userconfig-<name>`, so they take DNS labels of at most 52 characters. A previous Secret or key identifier equal to the current one fails at render. The apps API only defaults against the schema, and Helm enforces these patterns at install and upgrade. 5. **README.** The trade-off of `static`, the key Secret, the rotation with the pod replacement that the `OnDelete` strategy requires, the migration from Shamir step by step after the OpenBAO guide, and the one-time pod replacement after an upgrade so that existing pods get the label. ### Validation - `helm unittest .` in `packages/apps/openbao` passes 28 of 28 with helm-unittest 1.1.2, the version CI pins, and with 1.2.1. - Dropping the key identifier patterns, moving the Secret name limit to 51 or 53, or removing either equality guard turns the suite red each time. - Rendering this chart and then the vendored `openbao` chart with a template in `seal.keyId` now stops at the schema. Without the pattern the same two-stage render executed it, `lookup` included. - With default values, standalone and with `replicas: 3`, `helm template` renders the same as `main` apart from the six-line `extraLabels` block. - `make generate` in the package with cozyvalues-gen v1.7.0 and at the repository root with Go 1.27.1 leaves the tree unchanged. `make unit-tests` passes apart from seven BATS tests that fail the same way on a clean `main` under macOS, most of them because macOS ships no `timeout`. In `make test-controllers` only `internal/backupcontroller` fails, because its tests do not build on current `main` either, which #4768 fixes. `hack/check-commit-trailers.sh` passes and `cmd/api-gate` reports no sizeable API change. - Nothing here was run on a cluster. #4168 reports a Cozystack v1.6.3 cluster where a `replicas: 1` instance with `seal.type: static` came back `Sealed false` after `kubectl delete pod`. TLS between pods and an init or bootstrap job are not covered. Both are on the #2787 list and independent of this change. ### Screenshots No dashboard code change. The application form gains the `seal` group from the regenerated `openAPISchema` and `keysOrder`. ### Downstream repositories Walked the trigger map against the diff. The new `seal` field in `packages/apps/openbao/values.schema.json` reaches the hand-written `cozystack_openbao` resource of the Terraform provider. That is tracked in cozystack/terraform-provider-cozystack#44, which also needs `allowMigration` and the value constraints. The website reference page is generated from this `README.md` for the apps listed in its `Makefile`, and `openbao` is in `APPS`, so the page follows the next stable tag without a follow-up. The website pages that mention the apiserver label describe it in general terms and carry no OpenBAO workaround. There is no `hack/` change, no platform values change, no variant and no change to ApplicationDefinition semantics. - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [x] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: https://github.com/cozystack/terraform-provider-cozystack/issues/44 - [ ] [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(openbao): the OpenBAO app can auto-unseal with OpenBAO's static-key seal (`seal.type: static`) from a Secret that a cluster administrator creates in the tenant namespace, with n-1 rotation through `seal.previousSecretName` and `seal.previousKeyId`. Shamir stays the default, and changing the seal of an existing instance needs `seal.allowMigration`. Server pods now carry `policy.cozystack.io/allow-to-apiserver: "true"`, so HA instances no longer hang at start behind the apiserver egress policy. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * OpenBAO supports static sealing with keys stored in an existing Secret; Shamir sealing remains the default. * Configure current and previous keys to support key rotation in standalone and high-availability deployments. Keep the previous key until stored keys have been re-wrapped. * Seal-type changes are validated. Shamir-to-static migration requires explicit acknowledgement; static-to-Shamir migration is not supported. * Static-seal settings validate required key details and Secret names. Server pods receive a label needed by the configured API-server egress policy. * **Documentation** * Added guidance for configuring static sealing, managing Secrets, rotating keys, and migrating seal types. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3dc0d046b3 |
fix(postgres): cap the default shared_buffers at a quarter of the memory limit (#4724)
## What this PR does The chart renders no `shared_buffers`, and CloudNativePG does not set one either (its default parameters in `pkg/postgres/configuration.go` at v1.30.0, the operator version this repo ships, have no such key). PostgreSQL therefore starts with its built-in 128MB whatever the container gets. A full buffer pool is the whole memory limit on `t1.nano` and half of it on `t1.micro`, which is the chart's default preset. The pool is shared memory that the kernel cannot reclaim without swap. A small or idle database never fills it, but a dump, index scans over a large table or a replica catching up after a failover can, and then the instance is OOMKilled. Observed on a cluster running Cozystack 1.4.3, whose chart renders the same parameters, with a three-instance `t1.nano` database. A `pg_dumpall` run through `kubectl exec` inside the primary's container got that container OOMKilled. The dump client ran in the same container and shared its limit, so that first kill is weak evidence on its own. After the failover the old primaries could not rejoin. Their `pg_rewind` against the new 128Mi primary kept failing with `could not fetch file list: SSL SYSCALL error: EOF detected` until that primary's memory limit was raised to 512Mi, and then both rewinds completed. Below a 512Mi memory limit the chart defaults `shared_buffers` to a quarter of the limit with a 16MB floor, capped at the rendered memory request rounded down to whole MiB. CloudNativePG rejects a Cluster when `shared_buffers` exceeds that request, which can happen with memory overcommit. That is 32MB on `t1.nano` and 64MB on `t1.micro`. From 512Mi up a quarter is at least 128MB, so nothing is rendered and those clusters keep the PostgreSQL default exactly as before. An explicit `shared_buffers` in `postgresql.parameters` still wins at any size. The limit is read through `cozy-lib.resources.defaultingSanitize`, the helper that fills `spec.resources`, which is also how `packages/apps/mariadb/templates/_tuning.tpl` sizes the InnoDB buffer pool. Presets and partial `resources` overrides therefore resolve exactly as they do for the container. A quarter is the starting point the PostgreSQL documentation gives for a dedicated server with 1GB or more, and it advises a smaller share below 1GB, so this is a ceiling for small instances rather than tuning. Larger presets are left alone on purpose. Deriving a bigger pool for them is a tuning change that would restart every PostgreSQL instance on upgrade, and it does not belong in a fix. ### Upgrade impact `shared_buffers` needs a restart. On upgrade every cluster whose memory limit is below 512Mi and that does not set `shared_buffers` gets the new value, and that includes every cluster created with the default preset. The chart leaves `primaryUpdateStrategy` and `primaryUpdateMethod` at their CloudNativePG defaults, `unsupervised` and `restart`, so the operator restarts the replicas first and then the primary in place, and writes pause for the length of one PostgreSQL restart. A single-instance cluster is unavailable for that time. Clusters from 512Mi up render the same manifest as before and are not touched. ### Validation - `helm unittest` passes all 166 tests in 21 suites. The new suite covers the default preset, `t1.nano`, a `resources` override that sets only cpu, a 500Mi limit just under the threshold, the 16MB floor, `t1.small` and `t1.large` rendering no key, an explicit value on a small and on a large preset, and the other default parameters. Seven additional cases cover memory overcommit, rounding down a fractional MiB request, a request below the 16MB floor, explicit values and the unchanged 512Mi threshold. Four cases fail against the previous PR template. - Breaking the threshold comparison, the 16MB floor or the divisor, or dropping the new block, turns at least one case of the suite red each time. - `helm template` of the whole chart renders `t1.small` exactly as the base does, and the default preset differs only by `shared_buffers: "64MB"`. - `make generate` with cozyvalues-gen v1.7.0, the version CI pins, leaves `README.md`, `values.schema.json`, the Go types and the ApplicationDefinition unchanged, because `values.yaml` only gains a comment. - The new default was not exercised on a live cluster. ### Screenshots Not a UI change. ### Downstream repositories Walked the trigger map in `docs/agents/contributing.md` against the diff. It touches one chart template, a comment in `values.yaml` and a new test file. No package is added, renamed or removed, no field in `values.schema.json` is added, removed or renamed, and no default in `values.yaml` changes, so the Terraform provider has nothing to restate and the generated README is identical. Neither the website nor the provider mentions `shared_buffers`. No entry matches. - [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(postgres): default shared_buffers to a quarter of the memory limit with a 16MB floor, capped at the memory request, when the limit is below 512Mi and shared_buffers is not set. PostgreSQL's built-in 128MB was the whole memory limit on t1.nano and half of it on t1.micro, the default preset, so a dump, index scans over a large table or a replica resync could fill the buffer pool and get the instance OOMKilled. Affected clusters restart their instances once on upgrade, the primary last, with a short pause in writes. Clusters from 512Mi up and clusters that set shared_buffers explicitly are unchanged. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * PostgreSQL now automatically adjusts its default memory allocation for deployments with memory limits below 512 MiB, while retaining PostgreSQL’s default for larger limits. Explicitly configured values continue to take precedence. * **Documentation** * Clarified how the automatic memory allocation works, including its minimum and memory-request cap. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4b606036bb |
fix(mongodb): set the s3 bucket and prefix on the bootstrap restore
With bootstrap.enabled the chart rendered a PerconaServerMongoDBRestore without backupSource.s3.bucket. The operator CRD requires that field, so the API server rejected the restore and the HelmRelease failed. The prefix was missing too, and it matters just as much. For a backupSource restore the operator builds the PBM storage from backupSource.s3 alone and takes only the backup name from backupSource.destination, so a restore without the prefix would look for the backup at the bucket root. The restore now takes the bucket and prefix from the same helpers that fill the backup storage, so it reads from where the backups are written. Both places quote the two values, so a numeric bucket or prefix stays a string instead of making the resource invalid. The backup storage still leaves out an empty prefix, so running releases keep the same object. A destinationPath that names no bucket fails at render time instead of reaching the operator with an empty bucket. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
3ea7a95108 |
docs(openbao): document the static seal trade-off and the Shamir migration
With the static seal the key sits in a Secret in the same cluster as the data it unwraps, so the barrier is only as strong as access to Secrets in that namespace, to etcd and to backups. The README now says so before the reader picks a seal, and it says what the key identifier is for, since OpenBAO picks the key for stored data by it. Tenant roles grant nothing on Secrets and no pods/log, so a cluster administrator creates the key Secret and reads the log line that confirms a re-wrap. The StatefulSet uses the OnDelete strategy, so neither a seal migration nor the apiserver egress label reaches a running pod until it is replaced. The README describes the migration from Shamir step by step, following the OpenBAO seal migration guide, and says that pods created before the label have to be replaced once. The migration starts with a backup. The standalone file backend is not transactional, so its volume is copied with the server stopped or snapshotted atomically. The upgrade that sets allowMigration is the point of no return for the chart, and the README says how a cluster administrator backs out of it before any pod is replaced. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
bfbb5ac75f |
fix(openbao): constrain the seal values that reach the server config
The upstream chart passes the server config through tpl, and its start script replaces upper case placeholders such as HOSTNAME anywhere in the copied file. A key identifier with template delimiters would run as a template inside helm-controller, which runs as cluster-admin, and an upper case one could come out different on every pod. Both key identifiers now take lowercase alphanumerics, hyphens, dots and underscores. The Secret names become pod volume names of the form userconfig-<name>, which Kubernetes validates as DNS labels. Both name fields now take a DNS label of at most 52 characters, and the volume entries are quoted. The apps API only defaults against the generated schema, so Helm enforces these patterns when it installs or upgrades the release. A previous Secret equal to the current one mounts the same volume twice, which the API server rejects for the StatefulSet, and OpenBAO refuses to start when equal key identifiers name different key material. Both cases now fail at render. The deepcopy functions for the Seal type are regenerated as well. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
0c52dedbe2 |
feat(openbao): optional static-key auto-unseal and apiserver egress label
The OpenBAO app has no auto-unseal (#2787). Every restart of a server, after a node drain, an image bump or an OOM kill, leaves it sealed until an operator enters the Shamir key shares into each replica. In HA mode the pods also hang at start (#2793). Cozystack's allow-to-apiserver policy only admits egress from pods labelled policy.cozystack.io/allow-to-apiserver, the app never set that label, and service_registration "kubernetes" cannot reach the API server without it. A new seal value selects shamir, which stays the default, or OpenBAO's seal "static". With static the chart renders the static seal stanza into the standalone and the Raft config and mounts the key Secret named in seal.secretName through the upstream chart's server.extraVolumes. The chart never generates, stores or rotates the key, because a missed lookup would replace it and leave the data unreadable. seal.previousSecretName and seal.previousKeyId carry the n-1 key that the static seal accepts during a rotation. The server pods always carry the apiserver egress label. Changing the seal of an initialized instance is a seal migration that OpenBAO performs under bao operator unseal -migrate. The chart reads the seal in use from the config ConfigMap the previous release rendered and refuses a different seal.type unless seal.allowMigration acknowledges the migration. Moving away from static needs the old stanza kept with disabled = "true", which the chart does not render, so that direction is refused regardless. The README documents the key Secret, the rotation with the pod replacement that the OnDelete update strategy requires, and the log line that confirms the re-wrap. Signed-off-by: txmazing <txmazing@gmail.com> Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
9c0085c358 |
fix(postgres): cap shared_buffers at the memory request
CloudNativePG rejects clusters whose shared_buffers exceeds the memory request. Memory overcommit can put that request below both a quarter of the limit and the 16MB floor. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
6dbd76ae50 |
fix(postgres): cap the default shared_buffers at a quarter of the memory limit
The chart renders no shared_buffers, so PostgreSQL starts with its 128MB default whatever the container gets. A full pool is the whole memory limit on t1.nano and half of it on t1.micro, the default preset. The pool is shared memory that the kernel cannot reclaim without swap, so when a dump, index scans over a large table or a replica catching up after a failover fill it, the instance is OOMKilled. Instances below a 512Mi memory limit now default to a quarter of the limit, at least 16MB. From 512Mi up a quarter is at least 128MB, so those instances render exactly as before. shared_buffers needs a restart, so on upgrade CloudNativePG restarts every instance below 512Mi that does not set it explicitly. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
96b35893a5 |
fix(dashboard): mark a free-form field invalid when the schema rejects it
A JSON object that parses but fails the schema, for example on minProperties, left the textarea with aria-invalid false, and nothing linked it to the error list RJSF renders under the field, so assistive technology announced the field as valid. The field now reports the schema errors RJSF passes in and points aria-describedby at their list, unless hideError keeps that list from rendering. The red help text stays tied to input that is not a JSON object. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
d639113caf |
fix(dashboard): allow editing free-form objects in application forms
Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> Assisted-by: LLM |
||
|
|
87a64071a7 |
fix(cilium)!: take cilium's own addresses from a block kube-ovn does not hand out (#4778)
## What this PR does
fixes #3740.
on the kube-ovn variants cilium allocates no pod ips, but the agent
still takes its router ip (`cilium_host`, `CiliumInternalIP`) and the
gateway api ingress ip from its own allocation cidr. with `ipam.mode:
kubernetes` that's `Node.spec.podCIDR`, a /24 out of the same /16
kube-ovn hands pods from, so sooner or later kube-ovn gives a pod an
address cilium already holds. we hit it on three clusters, details in
https://github.com/cozystack/cozystack/issues/3740#issuecomment-6022394243.
**what changes**
- the kube-ovn variants run cilium with `ipam.mode: cluster-pool`, a /29
per node out of the last /21 of `networking.podCIDR` (`10.244.248.0/21`
for the default /16). the platform derives the block from
`networking.podCIDR` and passes the same range to kube-ovn as
`--default-exclude-ips`, so a fresh install never hands it to pods.
- the block sits inside the pod cidr on purpose. envoy reaches gateway
api backends from the node's ingress ip, and kube-ovn masquerades host
originated traffic only when the source is in `ovn40subnets`. with the
`100.65.0.0/16` pool from the issue a nodeport through the gateway
answered 200 only on the backend's node and 503 `upstream connect error
... connection timeout` on the other two. with the block inside the pod
cidr it's 200 from all three. worth a look on the ci side too. the ci
sandbox sets talos `podSubnets` to `100.65.0.0/16`, so cilium's ingress
ips there already come from outside `ovn40subnets`, and the gateway e2e
checks Accepted, Programmed and the lb service but sends no request
through a listener, so it wouldn't notice. with this pr the sandbox
takes its block from the pod cidr like everyone else.
- the end of the /16 is what kube-ovn reaches last. it hands out the
lowest free address and reuses released ones only when the free range
runs dry (`pkg/ipam/subnet.go` and `pkg/ipam/ip_range_list.go` at
v1.15.26). a /21 cut into /29s covers 256 nodes.
- the slim variants and guest clusters stay on `ipam.mode: kubernetes`.
cilium is the only cni there and `Node.spec.podCIDR` is the pod range.
- `networking.podCIDR` now needs /20 or wider on the kube-ovn variants,
otherwise the render fails and says why. an install that rendered before
can stop rendering at upgrade, hence the `!`. a /21 cut into /29s also
caps these variants at 256 nodes, a fresh install included.
**existing clusters**
a post-upgrade hook in the cilium chart moves them
(`templates/ipam-migration.yaml`, `files/ipam-migration.sh`). before it
touches anything it checks that the block has a /29 for every CiliumNode
and that it lies inside kube-ovn's default subnet, and stops with both
cidrs printed otherwise. a node cleared past the pool's capacity would
lose pod networking on its next agent restart, and a block outside the
subnet puts the agents outside `ovn40subnets` again. it has to be
post-upgrade. platform migrations run as the platform's pre-upgrade
hook, while every agent still runs `ipam.mode: kubernetes` and writes
`Node.spec.podCIDR` back into a cleared CiliumNode
(`pkg/nodediscovery/nodediscovery.go` at v1.19.5), so the move can only
happen after the cilium release switched the mode. the job runs on the
host network against the agent's own api endpoint (kubeprism on talos),
because it restarts the agents.
1. adds the block and every node's current ingress ip to `excludeIps` of
the default subnet, in one json patch with a `test` op, and reads the
subnet again on conflict, since kube-ovn rewrites that list into its own
order right after a patch. the old ingress ip goes in because cilium
1.19.5 keeps a former ingress ip in the agent's ipcache as
`reserved:ingress` after the move. we saw 2 to 3 such entries per node
hours later. old router ips leave the ipcache and go back to kube-ovn.
2. stops right there if an address in the block is still allocated,
names it, and moves no agent. it first checks the subnet status it
patched against, then waits until `status.v4availableIPrange` stops
offering the block, which is kube-ovn having applied the exclusion, and
then reads the `ips.kubeovn.io` objects of the default subnet. the
status alone can't answer it, kube-ovn leaves excluded addresses out of
`v4usingIPrange` (`GetSubnetIPRangeString` in `pkg/ipam/ipam.go` at
v1.15.26), so a pod still in the block would be invisible on a retry
where the exclusion is already in place. recreate those pods and the
next upgrade retry goes through.
3. one node at a time, clears the inherited /24 from
`spec.ipam.podCIDRs` (again with a `test` op, so a concurrent writer
fails the patch instead of losing an update), waits for the operator to
allocate a /29 from the block and restarts that node's agent. a node
that isn't ready is skipped and its agent picks up the block when it
starts. nodes already moved are left alone, so a retry after a timeout
just continues. the hook runs inside the cilium upgrade, so where
restarting every agent takes longer than the upgrade timeout, 10m by
default, the release first reports a failed upgrade and the move
finishes on the retry.
running pods keep their addresses. every agent restarts twice on
upgrade, once when the config change rolls the daemonset and once from
the hook, and while it restarts host to pod traffic on that node blips
for a few seconds because the router ip moves. a pod that sits on a
router ip today is reachable again after the move, the node no longer
holds that address. a pod on a node's current ingress ip keeps it and
that node keeps seeing it as `reserved:ingress`, so recreate it. the
hook logs those pods by name.
### Validation
- `helm unittest` passes cilium 23/23, platform 246/246 and kube-ovn
22/22. the new suites pin the rendered `cilium-config` keys per variant,
the hook shape, env and rbac, and the platform wiring for the talos and
generic branches, a custom pod cidr, host bits in the network address,
the refusals for a too small or non ipv4 cidr, and the slim variant.
- on a trial merge with main at
|
||
|
|
76c030d89f |
fix(dashboard): drop the resource map ConfigMaps once the map is unreadable (#4789)
## What this PR does Follow-up to #4612. The ConfigMaps tab takes its names from the application's resource map. React Query keeps the last successful list when a refetch fails, so when the resource map became forbidden or went away after a successful read, `useApplicationConfigMaps` went on parsing the old copy. The tab stayed with the old names, every listed ConfigMap showed an access error, and a malformed map kept its old error. A map that is unreadable from the start already hid the tab. Now a 403 or 404 on the resource map drops the names and the error whether or not an earlier read succeeded, so the tab goes away in both cases. Any other error is still shown on the tab, as before. The new tests are a page test with stale resource map data next to a 403, and hook tests that read the map and then refetch it with 403 and with 404, plus a malformed map followed by a 403. All four fail on main and pass here. `pnpm typecheck`, `pnpm test` (451 tests in 62 files) and `pnpm lint` pass. ### Screenshots No visual change. Once the resource map is unreadable, the page looks the same as for an application whose map was never readable, with no ConfigMaps tab. The tab itself is shown in #4612. ### Downstream repositories Walked the trigger map against the diff. It touches only the console hook that reads the resource map and its tests. No chart, values, schema, CRD or `hack/` file changes, and the console is vendored in this repository. - [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(dashboard): the ConfigMaps tab now goes away when the application's resource map stops being readable, instead of keeping the names from the last successful read ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * ConfigMaps are no longer displayed when access to the resource map is forbidden or the resource map is missing. * The ConfigMaps tab is excluded when the resource map cannot be accessed. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f203a47afe |
fix(backups): hold a Postgres backup until the plugin rollout is over (#4674)
<!-- 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 first BackupJob on a Postgres app whose `cnpg.io/Cluster` has never carried the barman-cloud plugin — a fresh platform-flow app, or one an in-place restore re-rendered — attaches the plugin and creates the `cnpg.io/Backup` in the same reconcile. Attaching the plugin changes the instance pod spec, so CNPG rolls every instance (`Pod rollout required: volumes: element plugins has been added`). The Backup then lands on an instance without the plugin sidecar (`requested plugin is not available: barman-cloud.cloudnative-pg.io`) or on one CNPG restarts under it (`instance manager was restarted during backup`). CNPG does not retry a failed Backup, so the BackupJob stays Running in `CNPGBackupTransientFailure` until `cnpgDefaultBackupDeadline` and fails. The first backup of each such Postgres is always lost; this was observed on a v1.6.3 cluster and reproduced against `main`. The driver now holds the Backup back until that rollout is over. Before attaching the plugin to a Cluster that has none, it records `PluginRolledOut=False` on the BackupJob, with `Ready=False` reason `WaitingForPluginRollout` for the user, and requeues; the next pass attaches the plugin. The Backup is created once CNPG reports the Cluster in its healthy phase and every instance pod (`cnpg.io/cluster`, `cnpg.io/podRole=instance`) was created after the condition and is Ready; the condition then turns True and `Ready` is cleared, as before. Recording the condition first means a lost status write leaves the plugin unattached for the next pass instead of a Cluster that looks ready while it rolls. A Cluster that already runs the plugin is not rolled by the driver and its Backup is created on the first pass, as today. The wait is bounded by the existing `cnpgDefaultBackupDeadline`, after which the BackupJob fails with the phase or pod CNPG is stuck on. Why this signal, checked against the CNPG 1.29 source and the 1.30 Cluster CRD vendored here: the Cluster status has no `observedGeneration` and its `Ready` condition is derived from `status.phase`, so right after the attach the phase still reads healthy from before until CNPG reconciles the new spec. `status.pluginStatus` does not help either, since CNPG also lists plugins referenced only from `externalClusters` there, which is exactly the state of a Cluster a restore re-rendered. A pod spec change is never applied in place (only a pending Postgres restart is), so the instances are recreated and pods newer than the attach are the ones running the sidecar. This reads pod metadata and CNPG's documented labels only, not the plugin's container or volume names, and needs no RBAC change: the controller already lists and watches pods. Tests: `TestReconcileCNPG_WaitsForPluginRolloutBeforeBackup` drives a Cluster without the plugin through the attach, a stale healthy phase over the old pod, a rolling phase over the recreated pod, and the healthy phase, and asserts no Backup exists until the last step. `TestReconcileCNPG_AttachedPluginDoesNotDelayBackup` covers a Cluster that already runs the plugin, and `TestReconcileCNPG_PluginRolloutWaitIsBounded` the deadline. With the gate disabled, the first and third fail; with only the phase checked and not the pods, the first fails at the attach pass. ### 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. --> Not applicable: no UI change. ### 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. --> The diff touches only `internal/backupcontroller` Go code: no package, values, chart, CRD, `hack/` tooling, telemetry metric or contributor convention. Walked against the trigger map, it reaches none of the repositories below, and the website documents no workaround for the lost first backup that this would make obsolete. - [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(backups): the CNPG backup driver no longer loses the first backup of a Postgres app whose Cluster had no barman-cloud plugin yet (a new platform-flow app, or one restored in place). It now waits for CNPG to finish rolling the instances onto the plugin before creating the cnpg.io Backup, and reports the wait on the BackupJob as Ready=False with reason WaitingForPluginRollout. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Reliability** * Backups wait for the backup plugin rollout to complete and cluster instances to become healthy before starting. * If rollout does not finish before the backup deadline, the backup job fails instead of waiting indefinitely. * Backups can start without waiting for a new rollout when the plugin is already attached. * **Backup Configuration** * When no server name is configured, backups use the cluster name and application UID, giving recreated applications distinct archive prefixes. * Existing server names are preserved when the plugin is already attached. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9be997b6e2 |
fix(cilium): size the agent memory limit for its BPF maps
cilium sizes its CT, NAT, neighbor and socket rev-NAT maps as a share of node RAM. They are preallocated LRU maps charged to the agent's memcg, so on nodes with around a terabyte of RAM or more the maps alone exceed the 4Gi limit and the agent is OOM-killed on every start after a reboot, leaving the node without pod networking. Raise the limit to the map ceiling (every map at LimitTableMax, both address families) plus the agent's own 4Gi budget. A per-node limit cannot be rendered into one DaemonSet, and pinning the map sizes would cut connection capacity on large nodes and reset conntrack and NAT state on upgrade, so the map sizes stay dynamic and a test keeps them unpinned. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
0a2bb5ef98 |
fix(cert-manager): enable the Gateway API solver in the host cert-manager (#4039)
## What this PR does With the platform Gateway enabled, the ClusterIssuers in `cert-manager-issuers` and the per-tenant Issuers rendered for `TenantGateway` carry an `http01.gatewayHTTPRoute` solver, but the controller refuses it: the vendored chart never turns Gateway API support on, so every HTTP-01 challenge behind the Gateway fails with `gateway api is not enabled` and no certificate is issued. This passes a `ControllerConfiguration` with `enableGatewayAPI: true` to the host cert-manager through the component values of its platform Package. The host ships the Gateway API CRDs on every variant and orders cert-manager after them, so the flag is safe there. It is deliberately not a default of the `system/cert-manager` chart. The same chart is installed into tenant Kubernetes clusters, where the CRDs are an opt-in addon, and cert-manager v1.20 exits at startup when Gateway API support is on and the CRDs are missing. The tenant HelmRelease in `packages/apps/kubernetes` already passes the same `ControllerConfiguration` when its `gatewayAPI` addon is enabled, so both clusters now opt in from the layer that knows whether the CRDs are there, and the chart itself stays neutral. The new tests pin both halves: the host Package carries the flag and the preconditions that make it safe, and the chart wires `config` through to the controller while shipping none by default. Closes #3725 ### Screenshots Not applicable, no UI changes. ### 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: ### Release note ```release-note fix(cert-manager): enable the Gateway API solver in the host cert-manager, so ACME HTTP-01 challenges succeed when the platform Gateway is enabled ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Supported system bundles can now use cert-manager’s Gateway API HTTPRoute solver to issue certificates. - Hosted deployments include the required Gateway API custom resource definitions, installed before cert-manager. - Gateway API solver support remains opt-in in environments that do not provide the required definitions, avoiding unintended changes to certificate management there. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9b0e25b1a0 |
fix(etcd-operator): gate VMServiceScrape on VictoriaMetrics operator CRD (#3960)
## What this PR does `packages/system/etcd-operator/templates/metrics.yaml` rendered a `VMServiceScrape` (`operator.victoriametrics.com/v1beta1`) unconditionally, so the `cozy-etcd-operator/etcd-operator` HelmRelease fails to install on any cluster that does not have the VictoriaMetrics operator CRDs, with `no matches for kind "VMServiceScrape" in version "operator.victoriametrics.com/v1beta1"`. That made the VictoriaMetrics operator a hard dependency of etcd-operator, which it should not be. This wraps only the `VMServiceScrape` document in a `.Capabilities.APIVersions.Has "operator.victoriametrics.com/v1beta1"` gate, following the precedent already established in `packages/system/linstor/templates/podscrape.yaml`. The `etcd-operator-metrics` Service keeps rendering unconditionally, so any scraper — a `VMServiceScrape` when the CRD is present, or something else entirely — can still target the `metrics` port. Note: this is a Cozystack-packaging-only issue. The upstream etcd-operator repo's own chart is unaffected — it already gates its scrape object. Added `packages/system/etcd-operator/tests/metrics_test.yaml` (`helm unittest`) pinning the new behavior: the Service always renders, and the `VMServiceScrape` renders only when the capability is injected. Kept this minimal — a pure capabilities gate with no new values key, matching linstor's style, rather than adding a values toggle. Follow-up (out of scope for this PR): `packages/extra/etcd/templates/podscrape.yaml` (a `VMPodScrape`) is unconditional the same way and would hit the same install failure on a cluster without the VictoriaMetrics operator. `packages/system/monitoring-agents` also ships several unconditional `VMServiceScrape`/`VMPodScrape` objects, though that package may already assume the VictoriaMetrics operator is present as part of the monitoring stack it belongs to. ### Screenshots Not applicable — no UI change. ### Downstream repositories - [x] No downstream repository is affected by this change ### Release note ```release-note fix(etcd-operator): stop requiring the VictoriaMetrics operator to install etcd-operator; the VMServiceScrape now only renders when the operator's CRD is present ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Metrics collection now works without the VictoriaMetrics operator API, while scrape configuration is rendered when that API is available. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
6192d5814b |
feat(local-storage): add opt-in OpenEBS LocalPV hostpath package (#3266)
## What this PR does Adds `packages/system/local-storage`, an optional package that vendors OpenEBS Dynamic LocalPV (chart 4.5.1) and registers it as the `cozystack.local-storage` PackageSource. A cluster without the DRBD and ZFS kernel modules can't run LINSTOR, so it has no dynamic PV provisioning, hostpath volumes cover that. They also fit deliberately node-local workloads. - StorageClass is `cozystack-local-hostpath`, `WaitForFirstConsumer`, `Delete` reclaim. It is not the default and doesn't take any existing class name. - Opt-in: a system bundle can list the package in `bundles.enabledPackages`, no built-in variant enables it. Alone it installs with `cozypkg add cozystack.local-storage`. - Vendored RBAC is cut down by `patches/rbac.diff` (reapplied by `make update`) to what volume reconciliation calls. Helper Pods, leader election Leases and analytics state stay in the install namespace, helpers are still privileged because they work on host directories. - Images stay on docker.io, pinned by index digest with `make update-image-pins`, nothing is rebuilt or mirrored. - `local-storage` scope is mapped to `area/storage` in `pr-labeler.yaml`, without it the title lands in `area/uncategorized` on every run. Tested with helm-unittest (StorageClass, PackageSource registration) and `tests/rbac_test.py` (effective permissions across the bindings). Not tried on a live cluster yet. Nothing installs the package by default, the container cluster e2e lane that will use it is a follow-up ([#3238](https://github.com/cozystack/cozystack/issues/3238)). ### Screenshots Not a UI change. ### Downstream repositories - [ ] No downstream repository is affected by this change - [x] [cozystack/website](https://github.com/cozystack/website) - follow-up: https://github.com/cozystack/website/issues/726 - [ ] [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: Website follow-up is an issue, the docs there describe a package that isn't merged yet. The diff adds a platform component and touches no platform values, ApplicationDefinition, tooling or `hack/` path, so nothing else in the trigger map matches. ### Release note ```release-note feat(local-storage): add opt-in cozystack.local-storage package that installs OpenEBS LocalPV hostpath and provides the cozystack-local-hostpath StorageClass, it is not enabled by any built-in variant and is not the default StorageClass. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added optional OpenEBS LocalPV Hostpath storage for provisioning persistent volumes on node-local storage. * Added a configurable `cozy-local-hostpath` StorageClass, disabled by default as the cluster’s default class. * Added controls for storage class creation, reclaim policy, hostpath quotas, and node deployment. * **Documentation** * Added guidance on installation, configuration, storage behavior, and uninstall considerations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
76b9e55102 |
fix(cert-manager): enable the Gateway API solver in the controller
With the platform Gateway enabled, the ClusterIssuers in cert-manager-issuers and the per-tenant Issuers rendered for TenantGateway carry an http01.gatewayHTTPRoute solver, but the controller only honours that solver when Gateway API support is switched on, and the vendored chart leaves it off. Every HTTP-01 challenge behind the Gateway then fails with "gateway api is not enabled" and no ACME certificate is issued. Pass a ControllerConfiguration with enableGatewayAPI to the host cert-manager through the component values of its platform Package. The host always ships the Gateway API CRDs and orders cert-manager after them, so the flag is safe on every variant. It is not a default of the system/cert-manager chart: the same chart is installed into tenant clusters, where the CRDs are an opt-in addon and a controller started with Gateway API support and no CRDs exits at startup. The tenant side keeps opting in on its own. Signed-off-by: Aleksei Sviridkin <f@lex.la> Assisted-by: LLM |
||
|
|
d61fed46cc |
feat(local-storage): add opt-in OpenEBS LocalPV hostpath package
LINSTOR needs the DRBD and ZFS kernel modules, so a cluster without them has no dynamic PV provisioning. OpenEBS LocalPV hostpath needs no kernel modules and also serves deliberately node-local workloads. The package is opt-in so it cannot change an existing cluster. Its class is named cozystack-local-hostpath so it cannot collide with an existing class name, and it is not the default so PVCs that omit storageClassName keep their current binding. The vendored RBAC is patched down to what volume reconciliation calls; helper Pods, Leases and analytics state stay in the install namespace. Images stay on docker.io, pinned by index digest. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
6addef4d3c |
fix(kubevirt): route the export proxy to the Service KubeVirt creates (#4637)
## What this PR does The VM export routes point at a Service that does not exist, and the tenant network policy would block the Ingress even if it did. KubeVirt creates the export proxy Service under the name `virt-exportproxy` (`VirtExportProxyName` in `pkg/virt-operator/resource/generate/components`, v1.9.0). The Ingress and the TLSRoute that publish `vm-exportproxy.<host>` name a `vm-exportproxy` Service, and nothing creates one. The backend was `virt-exportproxy` when the Ingress was added in |
||
|
|
8c1a78dc9b |
feat(operator): default system namespace memory via LimitRange (#3494)
## What this PR does
System components now get a default container memory limit, so their pod
cgroups carry a `memory.max` and drop out of the Talos userspace OOM
handler's victim set. `cozystack-operator` maintains a LimitRange named
`cozystack-system-defaults` in every system namespace it reconciles.
The Talos handler (v1.12+) scores cgroups with `memory_max.hasValue() ?
0.0 : {Besteffort: 1.0, Burstable: 0.5, ...}[class] * memory_current`
and drops everything that scores zero. Only a memory limit sets
`memory.max`, so only a limit takes a pod out of the candidate set. A
request just moves a pod from BestEffort to Burstable, which under the
default `strictCgroupClassOrdering: true` still means killed second.
`PriorityClass` does nothing here, because the handler reads cgroupfs
and never talks to the apiserver. Victim selection is also unrelated to
the trigger, so a tenant workload thrashing against its own limit can
drive node memory pressure while a system component with no limit is
what gets killed.
An audit of a running cluster found around 70 workloads across ~30
packages with at least one container missing a memory limit. Per-chart
resources would not cover that set, and would not keep covering it as
packages are added. So the LimitRange is written by
`reconcileNamespaces`, which already creates these namespaces and
already tells system from tenant. It also covers init containers, which
#3495 can't reach: one of them without a limit leaves the pod cgroup
without `memory.max`, whatever the main containers declare.
Tenant namespaces are skipped. Where the tenant chart's own
`tenant-range-limits` exists (only when the operator configured
`resourceQuotas`), a second LimitRange next to it would leave the result
to the order LimitRanger iterates them. Where it doesn't exist, a tenant
namespace is not the place for the platform to impose a default.
### Why the default is 32Gi
The handler grants immunity for `memory.max` existing at all, whatever
its value. So the ceiling doesn't need to be fitted, and 32Gi is high
enough that nothing plausibly reaches it. A ceiling low enough to bind
would turn a rare pressure-driven kill into a deterministic one for any
component whose real usage nobody measured. This repo already has one
such case, recorded in the `vertical-pod-autoscaler` values, where a
110Mi limit OOM-killed the updater and failed HelmReleases with it.
Scheduling is unaffected, since pods are scheduled against requests and
the paired default request is 32Mi. An operator who wants the ceiling to
bind can lower it, per component in that component's chart or fleet-wide
with the knob, once real usage is measured.
### When the default is withheld
A default below a container's own request makes that pod impossible to
admit, so the operator checks before it applies. It scans Deployments,
StatefulSets, DaemonSets, CronJobs, Jobs and Pods in the namespace, init
containers included, for a container that requests more than the
configured limit and declares no limit of its own. If it finds one, it
withdraws the managed LimitRange and records a hold in the
`operator.cozystack.io/system-memory-state` annotation on the Namespace.
The hold does not clear by itself when the blocker disappears, because
an empty scan looks the same as a pod between deletion and recreation.
Lowering a previously applied limit also creates a hold, even with no
visible blocker, because LimitRanger writes the defaulted limit onto
admitted pods and the scan can no longer tell which containers declare
one. A cluster administrator releases the hold by copying the
`<hold>:<target>` token from the state into the
`operator.cozystack.io/system-memory-ack` annotation. The token is used
once and only for that hold and target. If a blocker is still visible,
the token is rejected and the hold stays. Package reconciles are
scheduled at least every five minutes, so an acknowledgement takes
effect on a quiet cluster too. The reason and the token also appear in
the operator log under `withholding the default container memory limit`.
### Namespace another LimitRange already governs
If a system namespace already has a LimitRange that says anything about
memory, at the Container or the Pod scope, the operator writes nothing
there and withdraws what it wrote before. A second default leaves the
effective value to iteration order. A foreign `max` below this default,
a `min` above the default request or a `maxLimitRequestRatio` rejects
pods outright, which takes out a whole namespace. At the Pod scope the
collision can't be decided in advance, because it depends on how many
containers a future pod has. A LimitRange that bounds only cpu or
storage coexists normally. A same-named LimitRange without the
operator's managed-by label is treated as foreign and left alone.
This matters most in `kube-system`, which is in scope because
`cozystack-scheduler` installs there, and where a distribution's or an
administrator's own LimitRange is likeliest to sit.
### Disabling
Setting `cozystackOperator.systemNamespaceMemoryLimit` to `0` disables
the feature. On the next reconcile the operator removes every LimitRange
it owns cluster-wide, found by the managed-by label. While the feature
is on, it also removes its LimitRange from namespaces no active Package
targets any more. The safety state on the Namespace stays in both cases.
### Docs
`docs/operations/system-memory-limits.md` landed in #3496 ahead of this
code, so main describes a `4Gi` default and installer values that do
nothing yet. The page is corrected to the 32Gi default and now describes
withholding, the acknowledgement, and the foreign LimitRange rule.
### Not in this PR
linstor policy. #3824 extends the controller's existing exemption from
limits to the satellite, and a linstor limit here would contradict it,
so linstor stays with that PR.
The scan reads six workload kinds through `APIReader`, uncached, on
every reconcile of every system namespace. That cost is tracked in
#3977, since the system namespace set isn't known when the cache is
built and the right fix depends on a reconcile-rate measurement nobody
has taken.
### Verification
`go vet` and `go test` pass for `cmd/cozystack-operator` and
`internal/operator`, and `helm unittest` passes on the installer chart
for the knobs unset, set, and the limit at `0`. The reconciler tests
cover withholding on an oversized request, the hold surviving the
blocker's removal, the hold on a lowered limit, acknowledgement and
token rejection, every memory field of a foreign LimitRange at both
scopes, ownership by label, the disabled sweep, and a deleting Package
not creating policy.
The withhold design has not been run on a live cluster yet. An earlier
design of this PR was, and that run does not carry over.
### Screenshots
Not a UI change.
### Downstream repositories
I walked the trigger map against the diff.
`packages/core/installer/values.yaml` gains two keys, but the
`ansible-cozystack` trigger is for a required key or a rename of one the
role passes, and these are new, optional and default to `""`, so the
role is unaffected. Nothing else in the map is reached by the Go
changes, the operator template or the page.
- [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(operator): system namespaces now get a default container memory limit through a LimitRange maintained by cozystack-operator, which keeps system components out of the Talos userspace OOM handler's victim set. Tune it with cozystackOperator.systemNamespaceMemoryLimit (default 32Gi) and cozystackOperator.systemNamespaceMemoryRequest (default 32Mi); set the limit to 0 to disable it. The default is far above any real working set on purpose, because the handler grants immunity for a memory.max existing at all, not for its value. A namespace with a container that requests more than the limit and declares no limit of its own gets no default, and stays without one until a cluster administrator acknowledges the hold recorded on the Namespace (see docs/operations/system-memory-limits.md); lowering a previously applied limit needs the same acknowledgement. A namespace where another LimitRange already sets anything about memory is left alone. A container that declares its own memory limit is never changed.
```
|
||
|
|
84e8fcd477 |
fix(dashboard): re-authenticate on 401 instead of failing silently (#3115)
## What this PR does When the dashboard session expires, the console now sends the user back to sign-in instead of sitting on a stale page. This re-applies cozystack/cozystack-ui#34 by @IvanHunters onto the in-tree console, since the UI was vendored into the monorepo (#2963) and the standalone repo is being archived. It is part of the cozystack-ui retirement tracked in #3097. When the `kc-access` cookie expires, k8s API calls return 401. `K8sClient` now treats a 401 as an auth boundary and makes one top-level navigation to `/oauth2/sign_in?rd=<current-url>`. That path is the sign-in entry both gatekeeper modes serve. With OIDC enabled, oauth2-proxy goes straight to the provider (`--skip-provider-button`) and returns the user to `rd`. Without OIDC, token-proxy renders its token form there, but it does not honour `rd` yet, so the user lands on `/` after sign-in. The handler can be replaced through `K8sClientConfig.onUnauthorized` and does nothing outside the browser. A latch keeps concurrent 401s from redirecting more than once, and any 2xx from either fetch site re-arms it, so a handler that re-authenticates in place still sees the next expiry. Related: #2788. ### Verification `client.unauthorized.test.ts` covers the hook on the request and watch paths, the latch across concurrent 401s, the re-arm after a success, and the default handler's target and relative `rd`. `pnpm typecheck` and the console test suite pass. Original PR: cozystack/cozystack-ui#34, credited via `Co-authored-by`. ### Screenshots Not a UI change. ### 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(dashboard): on a 401 (expired session) the console now sends the user to sign in again instead of silently showing a stale page. With OIDC enabled the user returns to the page they were on; without OIDC they land on the start page after entering a token. ``` |
||
|
|
8ce3f853af |
fix(dashboard): drop the resource map ConfigMaps once the map is unreadable
React Query keeps the last successful list when a refetch fails. After the resource map became forbidden or went away, the hook went on parsing that old copy, so the ConfigMaps tab stayed with stale names, and a malformed map kept its old error, instead of the tab going away as it does when the map is unreadable from the start. Assisted-by: LLM Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com> |
||
|
|
3cce629535 |
feat(redis,postgres): certify the per-engine external hostname (#4627)
<!-- 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 Redis and postgres server certificates now carry the per-engine external name `<release>.<engine>.<apex>` (for example `redis-foo.redis.example.org`) next to the flat `<release>.<apex>` they already had. Both names appear under the same condition as before: TLS on and `external: true`, with a tenant apex present. A TLS-passthrough listener on the tenant Gateway forwards the handshake untouched, so the certificate the client checks is the engine's own. The design in #2815 routes databases by SNI under `*.redis.<apex>` and `*.postgres.<apex>`, and without the per-engine name in the SAN list a verifying client would fail the hostname check. Issuing both names now means a later move between the two hostname schemes reissues no certificate and keeps clients on the same trust anchor. This is the certificate step of #2815. Nothing renders a route yet. The SAN list changes, so an existing external TLS release gets its server certificate reissued once on upgrade, from the same CA. For redis the operator then rolls the Redis and Sentinel pods one at a time, the same way it handles a renewal. The READMEs say who can reach the new name through a passthrough listener. For postgres that is only clients using direct TLS negotiation (server 17+, `sslnegotiation=direct`), because by default a plaintext SSLRequest comes before the handshake. `redis-cli` needs `--sni`. MongoDB is not in this PR. PSMDB issues the certificate itself and its CRD has no field for extra SANs, so adding the mongos name means the chart taking over the certificate. That needs its own change. Covered by helm-unittest: the name is present with TLS and external on, and absent with external off, with TLS off, and in a tenant with no apex. ### 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 feat(redis,postgres): server certificates of external redis and postgres releases also carry the `<release>.<engine>.<apex>` name that the tenant Gateway's TLS-passthrough listeners are designed to route by. Existing external TLS releases get their server certificate reissued once on upgrade, from the same CA. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * PostgreSQL TLS certificates now cover both the tenant hostname and the per-engine `postgres` hostname for external connections. * Redis TLS certificates now also cover the per-engine `redis` hostname when external services are enabled. * **Documentation** * Clarified connection requirements for per-engine hostnames: PostgreSQL routing requires direct TLS negotiation, PostgreSQL 17 or later, and a compatible client; Redis clients must specify SNI. * Clarified that the hostnames may need to be mapped manually to the LoadBalancer address. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b1c84e9c06 |
feat(platform, tests): select E2E packages and declare controller dependencies (#4022)
## What this PR does E2E installs the whole platform on a fresh cluster for every PR, whatever the PR touches, so install time and flake surface go to packages the selected suites never use. This PR makes the E2E install take only what the selected suites need. `hack/select-install.sh --disabled <suites>` generates `bundles.disabledPackages` for fresh E2E installs using an ordinary platform variant. It keeps suite owners, runtime requirements and the install baseline, then follows their forward `PackageSource` dependencies. The baseline follows the unconditional waits in `hack/e2e-install-cozystack.bats` and stays seeded directly even where the graph reaches the same packages. It also keeps platform services that no suite owns but the rest of the platform uses: `multus`, `cozystack-scheduler`, `linstor-scheduler`, `flux-plunger`, `cozy-proxy` and `ouroboros` (the Kubernetes suites assert its hairpin NAT). Backup suites also keep `backupstrategy-controller` and its dependencies, and the gateway suite keeps `kubeovn-webhook`. The E2E job now selects the suites before it installs, and passes the complement to the platform Package as `disabledPackages`. The `full-e2e` label and an empty selection keep the full install, and the nightly and tag lanes pass no list. The platform sources also change: `mongodb-application` now depends on `mongodb-operator`, and `seaweedfs-application` on `objectstorage-controller`. These dependencies apply to regular platform installs and upgrades too. The new `mongodb-operator` edge also reaches reverse test selection: a change under `packages/system/mongodb-operator` now selects the `mongodb` suite instead of falling back to the full suite. Backup APIs and the gateway webhook remain suite-specific install requirements, so they add no edges. The disable list governs fresh installs because existing Packages carry `helm.sh/resource-policy: keep`. Optional packages still need `bundles.enabledPackages`. Packages that no suite owns and the baseline does not keep stay uninstalled on purpose, also when test selection answers with every suite (monitoring exporters like `goldpinger` and `kubeovn-plunger`, apps without a suite, provider-specific operators). Nothing in the run looks at them, so installing them only adds a readiness wait, and a change to such a package gets no install check from this gate. ### Measured One run per variant on a 32 vCPU / 128 GB VM, container lane, the CI artifacts of this PR, `postgres` suite selected, 55 packages disabled: - HelmReleases 111 to 77, pods 219 to 197 - pod requests 14.8 CPU / 37.7 GiB to 13.5 CPU / 35.7 GiB - host memory peak during install 33.4 GB to 30.2 GB - `install-cozystack` 26m33s in both runs - `postgres` suite 25m50s to 25m17s, passes on the reduced install Install time did not change. The removed packages are mostly operators and apps without instances, and what the install waits on (SeaweedFS, LINSTOR, CDI, the Cluster API providers, Keycloak) stays in the baseline. With one run each, differences of a minute or two are noise. The selection, the install closure and the workflow wiring are covered by bats tests (`hack/select-install_test.bats`, `hack/select-e2e_test.bats`, `hack/platform-packages_test.bats`) and by a helm-unittest suite in the platform chart. ### Screenshots Not a UI change. ### Downstream repositories The new dependencies ship in the platform chart. No coordinated changes are needed in 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 feat(platform, tests): E2E installs only the packages the selected suites need. MongoDB now depends on its operator, and SeaweedFS on the object storage controller. ``` |
||
|
|
5486d4ffcf |
fix(networking): harden webhook policy assumptions (#4058)
## What this PR does This is follow-up for [#3997](https://github.com/cozystack/cozystack/pull/3997). It reads ingress-nginx admission port from effective subchart values and refuses empty value, and fixes `kubeProxyReplacement=false` status because cozystack has no supported Service translator in that state. Gateway test now requires one real ClusterIP and keeps same ready Pod UID, IP and port for whole probe, so stale EndpointSlice, replaced Pod or wrong Service can't report deny. Policy selectors and two-ended deny from [#3997](https://github.com/cozystack/cozystack/pull/3997) stay unchanged, and this does not claim root cause for [#3983](https://github.com/cozystack/cozystack/issues/3983). ### Screenshots Not applicable. ### 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: ### Release note ```release-note fix(networking): use effective ingress admission port, report unsupported Cilium service routing correctly, and reject stale backend evidence in gateway tests ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Admission webhook connectivity checks now validate the configured Service and its exact backend endpoint throughout probing, improving failure detection. - Admission webhook policies now fail closed when the webhook port is not explicitly configured. - **Documentation** - Clarified that supported service routing requires `kubeProxyReplacement: true`. - Updated guidance to explain that disabling this setting or webhook policies does not provide a supported fallback. - **Tests** - Added coverage for missing webhook port configuration and strengthened networking stability checks. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ac4ab1c4a6 |
chore: drop private cluster and host names from the tree (#4461)
<!-- 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 I remove internal cluster names, a real hostname with its IP addresses, internal mirror hostnames and an internal product name from comments, an example file, test fixtures and a dead commented-out config block. Where a stand-in is needed I use neutral test-cluster wording, RFC 2606 domains or RFC 5737 documentation addresses. I also replace two real Keycloak usernames and a reviewer's name in code comments with placeholders, or drop them. Rendered output does not change. The test fixture hostnames change on the input and the expected side together, and the unit tests of every touched package still pass. ### 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 NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated backup, OIDC, autoscaling, and platform references to describe test environments without naming specific development clusters. * Replaced development-specific endpoints in the platform example with `example.org` and documentation-only IP addresses. * Updated OIDC username examples. * **Tests** * Updated fixture hostnames and expected URLs to use `example.org`. * **Chores** * Removed commented-out registry mirror entries; the active mirror configuration is unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
bc9a594e47 |
test(clickhouse): add backup API render assertions (#4011)
## What this PR does This adds unit tests for how the ClickHouse chart wires up the backup API that the default backup strategy calls. The Pod of the `cozy-default-altinity` strategy reaches the clickhouse-backup sidecar on the named port `ch-backup-api`. It authenticates with the credentials from the `<release>-backup-api-auth` Secret. The new suite is `packages/apps/clickhouse/tests/backup_api_contract_test.yaml`. It checks that: - the sidecar exposes `ch-backup-api` on 7171/TCP; - `API_USERNAME` and `API_PASSWORD` come from the `username` and `password` keys of `<release>-backup-api-auth`; - that Secret renders with `username: backup` and a non-empty password; - no auth Secret renders when backups are disabled. All of this is rendered by the chart, so the unit tests catch a regression on every pull request. The e2e assertions on the same wiring run only when test-impact analysis selects the ClickHouse suite. They stay as they are in `hack/e2e-chainsaw/clickhouse`, because they check the operator-created Pod and the admitted Secret, and rendering can't do that. ### Screenshots Not a UI change. ### 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 NONE ``` |
||
|
|
a1416655f4 |
fix(tenant): allow tenant egress to the KubeVirt export proxy
The vm-exportproxy Ingress is served by the ingress controller in tenant-root, and tenant egress is default-deny. Nothing allowed traffic to virt-exportproxy in cozy-kubevirt, so the Ingress stayed unreachable even with the right backend Service. The new policy renders wherever allow-to-cdi-upload-proxy does, but narrows the destination to the export proxy pods and their container port. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
c069a191fc |
fix(monitoring-agents): give kube-state-metrics a sized memory limit (#3846)
`kube-state-metrics` ran BestEffort with no resources at all: the
subchart ships `resources: {}` and our wrapper never set any. So it put
no `memory.max` on its pod cgroup, and the Talos userspace OOM handler
only ever selects cgroups that lack one, which left it permanently in
the victim set. Any tenant workload driving node memory pressure could
evict the source of every kube-state metric we alert on. Background is
in `docs/operations/system-memory-limits.md`.
## The numbers are measured
On a three node cluster with 322 pods and roughly 25.6k series, which is
the smallest we run: 14 day peak working set 307Mi, peak RSS 280Mi, mean
176Mi.
So the request sits above the mean at 192Mi rather than at the peak, and
the limit carries wide headroom at 2Gi. The headroom is deliberate: this
component's memory scales with the number of objects in the cluster, and
the `--metric-labels-allowlist=pods=[*],deployments=[*]` already in this
file widens every pod and deployment series further, so a cluster larger
than the one measured lands well above what was seen here. `2Gi` is 6.7x
that peak, which read as linear in object count is roughly 2100 pods and
170k series, so the limit stays a ceiling rather than a bound well past
the largest cluster this sizing was drawn from.
No CPU limit, following the node DaemonSet sizing in `c9ba7a3cc`, which
set one zero times across nineteen files. CPU is irrelevant to the OOM
ranking, and a ceiling that binds on a scraper turns into scrape
timeouts rather than backpressure.
## This supersedes #2632
#2632 proposes 256Mi, which is below the 307Mi peak measured on the
smallest cluster we have, so it would convert an eviction risk into a
reliable OOM kill. It also adds a `cpu: 200m` limit, which is the half
that buys nothing. The diagnosis there was right and it sat since May;
the numbers are what needed replacing.
## Checks
`packages/system/monitoring-agents/tests/kube_state_metrics_resources_test.yaml`
pins the limit, both requests, and the absence of a CPU limit. Mutation
checked: dropping the limit to 256Mi turns it red. The chart's suites
are 3 files and 6 tests, all green.
### Release note
```release-note
fix(monitoring-agents): kube-state-metrics now carries a memory request and limit sized from a measured peak, so it is no longer a permanent candidate for the node OOM handler
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Configured kube-state-metrics with requests of 50m CPU and 192Mi
memory, plus a 2Gi memory limit. No CPU limit is configured, allowing
CPU usage to scale as needed. These settings help ensure the service has
the memory and CPU resources it needs while keeping memory use bounded.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
5b585d6eda |
fix(operator): persist holds for unsafe memory defaults
An admitted Pod loses the distinction between explicit and defaulted limits. Its disappearance cannot prove a lower default is safe for its replacement. Keep that decision on the Namespace until an administrator acknowledges the particular target, without waiving current blockers or foreign policy ownership. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
30ecc41070 |
fix(operator): close memory policy safety gaps
Preserve foreign same-name objects and keep create-time SSA non-force. Only ignore failed pods controlled by terminal Jobs. Make the failure-containment test inject Apply errors for real. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
6a521f0c68 |
fix(operator): sweep memory defaults when disabled
List LimitRanges by the ownership label once per Package reconcile when the feature is disabled, and remove managed objects even in namespaces outside the current Package target set. Skip the per-namespace path while disabled and keep cleanup failures non-fatal to namespace reconciliation. Update the flag help, installer guidance, and operations documentation to describe the global cleanup boundary. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
f6dcb69b61 |
refactor(operator): withhold instead of raising, and drop the grace period
The raise-and-grace machinery cannot work: LimitRanger writes the raised limit onto the very container that justified the raise, so at that container's next admission it carries a limit, the scan skips containers that have one, and the raise has erased its own evidence. The ceiling then drops on schedule and the workload is rejected permanently. Any such machinery approximates admission logic ahead of admission, and that is incomplete by construction: a LimitRange default is one number for a whole namespace and cannot mean "the configured limit, or this container's request, whichever is larger". Only per-pod conditional defaulting expresses that, which is a mutating webhook and not this. The justification for the machinery had also expired. It was designed when the default was 4Gi, where withholding was self-perpetuating for VPA-managed namespaces. At 32Gi nothing in the tree approaches the ceiling -- the largest static memory request is 2Gi -- and VPA sets requests and limits together, so those containers carry their own limit and were never blockers. So a namespace holding a container that requests more than the configured limit with no limit of its own is now left with no default at all, and anything written there earlier is withdrawn. It fails in the safe direction, which is the whole argument: a namespace with no memory.max is a namespace behaving as it did in every release before this, while a default below a request is a namespace whose workload cannot be admitted. It needs no stored state and no timer, because withholding is not a decision that has to survive between reconciles -- once the oversized request is gone the default is applied again on the next pass. Gone with it: the grace period, the clear-since annotation and its clamp, the injected clock, containerMemoryDefault, raisedCeiling, and fourteen tests. The scan stays, because deciding whether to apply or withhold is what it was always for. The foreign-LimitRange ownership guard, the disable path and the 32Gi default are untouched. The residual sharp edge is stated rather than machinery: a workload whose only evidence is a live Pod, built from a custom resource with no template this scan can read, can still be missed if the scan runs while that Pod is absent, and applying the default then rejects its recreation. At 32Gi that is unreachable in this tree. It becomes reachable if an operator lowers the knob below such a workload's request, which the flag help, values.yaml and the operations page now say plainly. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
81c12bc953 |
fix(operator): withhold on any foreign memory policy, not two fields
The coexistence guard treated a namespace as already-governed only when another LimitRange carried a container memory default or max, and said so in its comment. That is not the set of fields that collide with what this reconciler writes. A container min above the default request rejects the pod, and so does a maxLimitRequestRatio, since a 32Gi default against a 32Mi default request is a ratio of 1024:1 and no cap allows it. So the namespace-wide admission failure the guard exists to prevent stayed reachable through fields the guard did not read, and kube-system is where it is likeliest, this feature having deliberately brought it in scope. Enumerating the colliding fields does not fix it, because at the Pod scope the collision is not decidable here. Whether a per-container default of 32Gi breaches a pod-wide max depends on how many containers the pod has, and the pods in question do not exist yet: any arithmetic about them is a guess, and a wrong guess costs a namespace its entire pod admission. So the rule is ownership rather than arithmetic. If anything else in the namespace has an opinion about memory, at either scope and in any field, the namespace is not this reconciler's to default. Narrow still on the resource, because nothing here writes cpu or storage and a LimitRange bounding those must keep coexisting. This is the rule the tenant-namespace exclusion already rests on. The pod scan no longer skips Failed pods, only Succeeded. A pod that ran to completion is never recreated from its spec, but an eviction or a node crash leaves a Failed carcass holding the same oversized request that its controller will recreate, and that carcass is frequently the only surviving evidence the namespace needs a raised ceiling, since the shape the scan looks for cannot be admitted once the namespace has settled. Dropping it lowered the ceiling under a workload that was coming back, which is what the grace period exists to prevent. A grace stamp dated ahead of the clock is clamped to now. Unclamped it held the ceiling for the skew plus the grace period, silently, so an hour of skew bought ninety minutes instead of thirty. Clamping never shortens the wait, it only stops it being extended. The two flag defaults are named constants the flag registration and its test both read. The test previously duplicated the literal and still asserted 4Gi after the default moved to 32Gi, so it misinformed a reader about the shipped value and pinned nothing. The disable claim is narrowed to what the code guarantees. "Removes any previously created one" was universal, but the reconciler only runs for namespaces an active Package still targets, so a namespace whose Package was removed keeps its LimitRange. The flag help, values.yaml and the operations page now say that, and name the managed-by label for finding leftovers by hand. The test asserting a Pod-scoped memory max should be ignored is gone, since under the ownership rule a Pod-scoped memory bound withholds. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
bcfa50d7e9 |
test(e2e): install only the packages the selected suites need
The E2E job installs the whole platform on a fresh cluster before it knows which suites will run, so every PR pays install time and flake surface for packages its suites never use. Select the suites first and pass the complement computed by select-install.sh to the platform Package as disabledPackages. The full-e2e label and an empty selection keep the full install, and the nightly and tag lanes pass no list, so they are unchanged. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
974aaa0bac |
test(hack): regenerate the selective install fixture
Two optional Cluster API providers, Proxmox and in-cluster IPAM, were added to the platform sources after the fixture was generated. The drift test compares the fixture with the current disable list, so it failed until they were added. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
f46a9797b8 |
fix(hack): keep platform services in selective installs
Dropping every package that no suite owns also drops services the rest of the platform relies on: multus for secondary networks, the cozystack and LINSTOR schedulers, flux-plunger, cozy-proxy and ouroboros, whose hairpin NAT the Kubernetes suites assert. Nothing waits for them, so no PackageSource edge reaches them, and adding one would also change reverse test selection. The remaining packages stay uninstalled on purpose, also when test selection answers with every suite: nothing in the run looks at them, so installing them only adds a readiness wait. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
59555c895e |
fix(hack): retain controllers and APIs for selective installs
Tenant HelmReleases need a shard reconciler, and the root SeaweedFS release creates COSI resources even when no backup suite is selected. Without those controllers, a fresh reduced install cannot become ready. The Kubernetes suites create worker pools through KubernetesNodes, and MongoDB renders an operator-owned CR. Selecting only the cluster API or application chart drops resources those suites require. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
361893e6f4 |
fix(hack): retain backup APIs in selective installs
Backup roundtrip suites create Bucket and backup resources that the application graph does not require. A graph-only closure therefore omits the APIs those suites need on a fresh install. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
610578be16 |
test(hack): verify selective install contract
A missing suite owner or a discarded discovery failure can leave a reduced install without the APIs its tests exercise. Graph-only checks cannot establish that the chart preserves the requested package set. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
b6c33926dc |
fix(dashboard): re-arm the 401 latch once the session recovers
unauthorizedHandled was set once per client and never cleared, so it collapsed every later 401 too, not just the concurrent ones it was meant to. Harmless for the shipped default handler, which navigates away and takes the client with it, but the onUnauthorized hook exists precisely so a caller can re-auth in place, and such a handler would fire exactly once for the client's lifetime and then go silent. Clear the latch on any 2xx from either fetch site: a success means the session is alive again, so the next 401 is a new episode. The storm guard still holds, since concurrent 401s have no success between them. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
7e5f9ab152 |
fix(dashboard): re-auth via /oauth2/sign_in so token-proxy mode works
The dashboard gatekeeper runs one of two mutually exclusive proxies, chosen by the cluster's oidc-enabled flag (templates/gatekeeper.yaml). /oauth2/start exists only in oauth2-proxy. The token-proxy branch registers only sign_in, sign_out and userinfo under the /oauth2 prefix, so there /oauth2/start falls through to the catch-all reverse-proxy handler, which does not re-verify the token. With a present but expired cookie it proxies the request to the console upstream, the SPA reloads, gets a 401 again and loops. /oauth2/sign_in is the one entry point both modes implement, so the default 401 handler goes there. oauth2-proxy's sign_in hands straight off to OAuthStart because the gatekeeper passes --skip-provider-button, and it resolves rd through the same app-director, so the OIDC path behaves as it does through /oauth2/start. token-proxy serves its token form there. It does not honour rd yet, so after sign-in the user lands on / instead of looping. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
badbec63fe |
test(dashboard): cover the 401 -> oauth2-proxy redirect path
The 401 handling is what brings an expired session back to sign-in,
and a regression in it stays invisible until a cookie expires. The
test drives K8sClient through the vi.stubGlobal("fetch", ...) harness
the sibling k8s-client tests use. It checks that the hook fires once
per 401 from both the request and the watch fetch sites, once across
concurrent 401s, never on other responses, and that the default
handler passes the current location as a relative rd, which
oauth2-proxy's open-redirect check accepts.
Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
8bf78d3bfe |
fix(dashboard): redirect to oauth2-proxy on 401 instead of failing silently
When the kc-access cookie expires, k8s API calls return 401 and the SPA stays on a stale page until the user clears the cache by hand. K8sClient now treats 401 as an auth boundary and makes one top-level navigation into the oauth2-proxy sign-in flow, passing the current location as rd so the user comes back to the same page. The handler can be replaced through K8sClientConfig.onUnauthorized and does nothing outside the browser. The unauthorizedHandled latch keeps concurrent 401s from redirecting more than once. Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963), since the source repo is being archived. Related: #2788. Co-authored-by: IvanHunters <49371933+IvanHunters@users.noreply.github.com> Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
3d901ca9b4 |
test(clickhouse): add backup API render assertions
The port the backup strategy dials, the two secretKeyRefs the clickhouse-backup sidecar authenticates with, and the auth Secret that backs them are all rendered by this chart. A helm-unittest suite catches a regression in them on every pull request, while the e2e assertions on the same wiring run only when test-impact analysis selects the ClickHouse suite. The e2e assertions stay, since they check the operator-created Pod and the admitted Secret. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
9fe93a3d7e |
docs(operator): fix three claims that overstated what the code does
The static-pod note said containers built from an on-disk manifest are unaffected "in both directions". True of the container, which the kubelet runs from disk whatever the API says, but the mirror pod is an ordinary API object that passes admission like any other, so a static pod declaring a memory request above the ceiling with no limit of its own has its mirror pod rejected -- leaving the container running with no representation in the API. Worth naming, since kube-system is in scope by design. The scan does cover it: a mirror pod is a live Pod. The ReplicaSet exclusion was justified on live ones being copies of their Deployment's template. That does not reach an ownerless ReplicaSet, which at replicas 0 has neither pods to be seen through nor a Deployment template standing in for it. It stays out of scope -- nothing here ships a bare ReplicaSet -- but as a gap left open rather than one the stated reason closes. The delete guard's comment claimed the enabled apply always adopts and stamps a same-named foreign object. It does not when that object's spec already matches, because then the apply is skipped entirely. The outcome is the same one the guard gives on purpose, but it is reached by the apply not running. Assisted-By: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |