Commit Graph
2461 Commits
Author SHA1 Message Date
Myasnikov Daniil d00ae9d14c feat(platform): add cozystack.customizer system package
Introduces a new opt-in system package that provisions an admin-facing
GitOps surface: a Flux GitRepository + Kustomization in cozy-system
reconciling an admin-owned git repo of cluster customizations via a
curated ServiceAccount.

The admin uses this to manage Package CR patches (e.g. OIDC enable,
MetalLB option overrides) and bring their own HelmReleases / Keycloak
realm imports / NetworkPolicies as code in a repo they own, rather
than via in-cluster kubectl patch.

Wired into the platform chart via a single `customizer.enabled` flag
in values; off by default. When enabled, emits the cozystack.customizer
Package CR through the existing bundles/system.yaml path and respects
bundles.disabledPackages.

Curated ClusterRole grants patch (not delete) on packages.cozystack.io,
read-only on platform HelmReleases, full ownership of PackageSources +
admin-owned namespaces (default: cozy-customizer), and Keycloak realm
CRs. Explicitly does NOT grant delete on Packages, anything on CRDs,
the cozystack-controller Deployment, or kube-system.

NOTE: this MVP ships the GitOps surface only. The field-ownership
contract documented in the package values is currently advisory; a
validating admission webhook on packages.cozystack.io is the planned
enforcement (kustomize-controller hardcodes client.ForceOwnership on
Apply, so SSA conflict detection cannot block silent ownership
transfer at the Flux layer). Risk callout is inline in the values
block.

Includes helm-unittest coverage for the bundle wiring (off, on with
values, disabledPackages override).

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-26 00:54:13 +05:00
Andrey Kolkov debb9f85db feat(platform): add implementation of backup-strategy controller for etcd (#2641)
<!-- 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 `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### 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. -->

### 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
add implementation of backup-strategy controller for etcd
```

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

* **New Features**
* Cluster-scoped Etcd backup strategy CRD (S3-only) plus controller
support for BackupJob (snapshot → Backup) and destructive in-place
RestoreJob flows; snapshot metadata persisted and destination validation
enforced.

* **Documentation**
* Complete etcd backup/restore README and admin/tenant scenario guides.

* **Examples & Tests**
* End-to-end demo scripts (helpers, stepper, cleanup, runner), Bats e2e,
and extensive unit tests.

* **Chores**
* RBAC updates for etcd access, chart version bumped, demo env files
added to .gitignore.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2641?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-22 16:06:02 +04:00
Andrey Kolkov d80f67e799 feat(platform): add implementation of backup-strategy controller for etcd
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-22 14:35:08 +04:00
myasnikovdaniil e841688082 fix(postgres): accept integer values for postgresql.parameters in schema (#2687)
## What this PR does

PostgreSQL server parameters like `max_connections` are natural
integers,
but the chart's schema declared them as `{type: string}` only. When
users
supply bare integers (e.g. `max_connections: 100`) — either directly in
Application CR specs or via the UI — Helm rejects the values at schema
validation time before template rendering, causing HelmRelease failures.

The Helm template already handles both types via `{{ $value | toString |
quote }}`,
so the fix is to teach the schema to accept both.

### Approach

The schema files (`values.schema.json` and the embedded `openAPISchema`
in `postgres-rd/cozyrds/postgres.yaml`) are generated from `values.yaml`
annotations via `cozyvalues-gen`. Hand-editing them does not survive
`make generate` — same regeneration trap that caught #2466 (issue
#2597).

`cozyvalues-gen` v1.4.0 (cozystack/cozyvalues-gen#25) adds an
`intOrString` type alias for map values that emits
`additionalProperties.anyOf: [integer, string]` plus the Kubernetes
`x-kubernetes-int-or-string: true` marker.

### Changes

- `packages/apps/postgres/values.yaml`: annotation flipped from
  `@field {map[string]string}` to `@field {map[string]intOrString}`,
  description updated to note integer values are accepted.
- Regenerated artifacts: `values.schema.json`, embedded `openAPISchema`
  in `packages/system/postgres-rd/cozyrds/postgres.yaml`,
`packages/apps/postgres/README.md`, and
`api/apps/v1alpha1/postgresql/types.go`
  (Parameters is now `map[string]intstr.IntOrString`).
- `packages/apps/postgres/templates/db.yaml`: `$dangerousParams` list
  extended with `archive_cleanup_command` and `recovery_end_command`
  (same RCE surface as `archive_command` / `restore_command`).

### Verification

- `helm template` with `max_connections: 200` (int) and
`max_connections: "200"`
  (string) produce identical render output.
- Dangerous-param block list rejects `archive_cleanup_command` and
  `recovery_end_command` with the expected `SECURITY:` error.
- `make test` in `packages/apps/postgres/`: 13/13 pass.
- `make generate` is idempotent — schema files don't drift on regen.

### Release note

```release-note
fix(postgres): accept integer values for postgresql.parameters to prevent HelmRelease failures when parameters are supplied as unquoted integers; also block archive_cleanup_command and recovery_end_command (RCE surface).
```

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

* **New Features**
* PostgreSQL parameters now accept integer or string values; integers
are automatically coerced to strings.

* **Security**
* Enhanced parameter filtering blocks additional dangerous settings:
archive_cleanup_command and recovery_end_command.

* **Documentation**
* Updated docs and charts to describe int-or-string parameter support
and revised blocked-parameter guidance.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2687?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-22 08:58:22 +05:00
myasnikovdaniil ee9d1544e1 fix(platform): migrate ephemeralStorage to diskSize via pre-upgrade hook (#2688)
## Problem

PR #2454 renamed `nodeGroups[*].ephemeralStorage` →
`nodeGroups[*].diskSize` with
a hard `{{ fail }}` guard. Any cluster whose HelmRelease still carries
the legacy
field cannot be reconciled by Flux at all — unrelated control-plane
changes and
MachineHealthCheck remediations are also blocked.

## Solution

Add platform migration 41 that runs as a pre-upgrade hook before any
chart
resources are applied. The migration walks every
`kuberneteses.apps.cozystack.io`
Application CR cluster-wide and renames `nodeGroups[*].ephemeralStorage`
to
`nodeGroups[*].diskSize`, preserving the user's value.

- Idempotent: a second run is a no-op (field is already absent)
- Best-effort: a failed patch is logged; the version stamp stays at 41
so the
  migration retries on the next platform upgrade
- Bumps `migrations.targetVersion` 41 → 42

The chart-side guard remains in place with an updated error message that
directs
operators to the migration job logs if the field somehow reappears
post-upgrade.

## Testing

Verified on dev cluster: migration correctly renames `ephemeralStorage`
values
in existing Application CRs. All 123 helm unit tests pass.

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

## Release Notes

* **Documentation**
* Platform migration 41 now automatically handles the `ephemeralStorage`
to `diskSize` field rename for Kubernetes node groups
* Migration is transparent during upgrade with no manual configuration
changes required
  * Error messages updated with clearer troubleshooting guidance

* **Tests**
  * Updated test assertions for migration error messages

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2688?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-21 16:37:26 +05:00
Myasnikov DaniilandIvanHunters 14e196cf22 fix(postgres): drive postgresql.parameters int-or-string from the generator
Ivan's original schema edits were correct in intent but lived in the
generated artifacts (values.schema.json and the embedded openAPISchema
in postgres-rd/cozyrds/postgres.yaml). `make generate` reverts them
because values.yaml's `@field {map[string]string}` annotation is the
source of truth -- same regeneration trap that caught #2466 (issue #2597).

cozyvalues-gen v1.4.0 (cozystack/cozyvalues-gen#25, merged 2026-05-21)
adds an `intOrString` type alias for map values that emits
`additionalProperties.anyOf: [integer, string]` plus
`x-kubernetes-int-or-string: true`. Move the change to source by
flipping the annotation, then regenerate:

- packages/apps/postgres/values.yaml: `@field {map[string]intOrString}`
  with updated description noting integer values are accepted.
- packages/apps/postgres/values.schema.json: regenerated -- now carries
  anyOf + x-kubernetes-int-or-string: true.
- packages/system/postgres-rd/cozyrds/postgres.yaml: embedded
  openAPISchema regenerated to match.
- packages/apps/postgres/README.md: regenerated type column.
- api/apps/v1alpha1/postgresql/types.go: Parameters is now
  `map[string]intstr.IntOrString`.

The result survives future regenerations. The template's
`{{ \$value | toString | quote }}` already coerces both inputs to
quoted strings, verified end-to-end with helm template against both
`max_connections: 100` (int) and `max_connections: "100"` (string) --
identical render output. Dangerous-param block list still rejects
archive_cleanup_command and recovery_end_command additions from the
prior commit.

Co-Authored-By: IvanHunters <xorokhotnikov@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-21 16:04:07 +05:00
IvanHunters 388e823916 fix(postgres): block archive_cleanup_command and recovery_end_command
Add archive_cleanup_command and recovery_end_command to the dangerous
parameters blocklist in db.yaml. Both are PostgreSQL shell-exec hooks
identical in threat model to archive_command/restore_command and must
not be settable by tenants.

Also update the BLOCKED list in values.schema.json, values.yaml, and
the embedded openAPISchema in cozyrds/postgres.yaml to reflect the
two new blocked parameters.

Additionally update the embedded openAPISchema in cozyrds/postgres.yaml
to allow integer values for postgresql.parameters (anyOf string|integer),
matching the fix already applied to values.schema.json.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-21 16:04:07 +05:00
IvanHunters 8c89556e79 fix(postgres): accept integer values for postgresql.parameters in schema
The template already coerces all parameter values to strings via
'toString | quote', but the JSON schema had 'additionalProperties: {type: string}'
which caused Helm schema validation to fail before the template ran
when users passed integer values (e.g. max_connections: 100).

Accept both string and integer types in additionalProperties so that
existing configurations with unquoted numbers continue to work.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-21 16:04:07 +05:00
myasnikovdaniil 243d20ea30 fix(cluster-api): fix kamaji OOM and set limits on unset providers (#2708)
## What this PR does

Fixes resource specs on CAPI provider pods in `cozy-cluster-api`.

**Kamaji control-plane-provider — fix OOMKilled crashloop.**
The override in
`packages/system/capi-providers-cpprovider/templates/providers.yaml`
targeted `containers[name=manager]`, but the upstream container in
`docker.io/clastix/cluster-api-control-plane-provider-kamaji` is named
`controller`. The CAPI operator merges container overrides by container
name, so the intended `cpu: 1 / memory: 1024Mi` limits were silently
dropped and the upstream default of `memory: 128Mi` was used. The kamaji
controller pod therefore restarts repeatedly with `exit code 137`
(OOMKilled), even at idle.

**Core / bootstrap / kubevirt providers — add limits.**
Upstream `*-components.yaml` for these three providers ships with no
`resources:` block, and cozystack added no override. The pods run
BestEffort and are first to evict under memory pressure. Set modest
limits (`500m / 256Mi`) and requests (`10m / 64Mi`) matching typical
CAPI controller-manager footprint.

### Release note

```release-note
fix(cluster-api): fix the resource override for the kamaji control-plane-provider (was silently ignored due to a wrong container name, causing OOMKilled restarts) and set requests/limits for the core, kubeadm-bootstrap, and kubevirt-infrastructure providers (were running BestEffort).
```

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

## Summary by CodeRabbit

* **Chores**
* Updated provider deployment configurations to establish resource
management parameters (CPU/memory requests and limits) for container
deployments across multiple provider types, improving scheduling
optimization and resource predictability
* Standardized container naming conventions in provider templates for
consistency

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2708?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-21 15:14:21 +05:00
Myasnikov Daniil 6ae66f72a6 fix(cluster-api): fix kamaji OOM and set limits on unset providers
The kamaji control-plane-provider override in templates/providers.yaml
targeted containers[name=manager], but the upstream container is named
"controller". The CAPI operator merges container overrides by name, so
the intended cpu: 1 / memory: 1024Mi limits were silently dropped and
the upstream default of memory: 128Mi was used, producing repeated
OOMKilled restarts of the kamaji controller (exit code 137).

The other three providers (core, bootstrap, infraprovider/kubevirt)
ship upstream with no resources block and have no cozystack override,
so they run BestEffort and are first to evict under memory pressure.
Set modest limits (500m / 256Mi) and requests (10m / 64Mi) matching
typical CAPI controller-manager footprint.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-21 12:57:02 +05:00
IvanHunters ed1bb53d2c fix(platform): migrate ephemeralStorage to diskSize via pre-upgrade hook
Add pre-upgrade migration 41 that walks all kuberneteses.apps.cozystack.io
Application CRs and renames nodeGroups[*].ephemeralStorage to diskSize,
preserving the user's value.

Without this migration, clusters upgraded after PR #2454 either fail to
reconcile (hard fail blocks all Flux operations) or silently lose the user's
disk size setting (default 20Gi replaces whatever was configured).

The migration is idempotent and best-effort: a failed patch is logged and
leaves the version stamp at 41 for retry on next upgrade.

Update the chart-side guard error message to direct operators to the
migration job logs when the field appears post-upgrade (regression detector).
Bump migrations.targetVersion 41 -> 42.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-21 12:14:55 +05:00
myasnikovdaniil 9c87996da8 fix(kafka): remove ZooKeeper PVCs on uninstall (#2679)
## What this PR does

Aligns ZooKeeper storage cleanup with the existing Kafka broker behavior
so deleting a Kafka release reclaims all of its persistent storage. The
Strimzi `Kafka` CR template already set `deleteClaim: true` on the
broker JBOD volume, but the ZooKeeper persistent-claim was left at
`deleteClaim: false`. As a result, removing a Kafka application freed
the broker PVCs while orphaning the ZooKeeper PVCs in the tenant
namespace, requiring manual cleanup before the same release name could
be reused.

The change is a single field flip in
`packages/apps/kafka/templates/kafka.yaml` on the
`spec.zookeeper.storage` block.

### Behavior change

Users who previously relied on orphaned ZooKeeper PVCs as an implicit
"soft delete" for ZooKeeper state recovery should be aware that
uninstalling a Kafka release now also removes the underlying ZooKeeper
storage. Take a backup before deletion if ZooKeeper data retention is
required.

### Screenshots

Not applicable — no UI changes.

### Release note

```release-note
fix(kafka): ZooKeeper PVCs are now reclaimed when a Kafka release is deleted, matching the existing Kafka broker storage behavior.
```


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

* **Chores**
* Kafka storage behavior updated so persistent volumes are automatically
removed when claims are deleted, changing storage lifecycle for
deployments.

* **Tests**
* End-to-end cleanup adjusted to remove the Kafka resource and verify
volumes are reclaimed automatically, adding waits to confirm PVC binding
and removal.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2679?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-21 11:37:55 +05:00
Andrei KvapilandClaude 1c059b3ba3 chore(platform): drop dashboard.cozystack.io CRDs via migration 40
Existing clusters still have the legacy dashboard.cozystack.io CRDs
installed even after the dashboard controller has been removed.
Add a platform migration that deletes them and bump targetVersion
to 41.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-05-20 16:33:21 +02:00
Andrei KvapilandClaude 4d5486cd84 chore(controller): remove dead dashboard controller
The dashboard controller used to project ApplicationDefinitions
into a set of dashboard.cozystack.io CRDs (Sidebar, Factory,
Navigation, Breadcrumb, CustomFormsOverride, CustomFormsPrefill,
CustomColumnsOverride, MarketplacePanel, TableUriMapping,
CFOMapping, BreadcrumbInside) consumed by the legacy UI.

cozystack-ui now reads ApplicationDefinitions directly, so the
controller, its API types, the generated CRDs and the related
RBAC rules are no longer used.

Removes:
- internal/controller/dashboard
- api/dashboard/v1alpha1
- packages/system/cozystack-controller/definitions/dashboard.cozystack.io_*.yaml
- dashboard.cozystack.io wiring from cmd/cozystack-controller/main.go
- dashboard.cozystack.io RBAC rules from cozystack-controller and cozy-dashboard charts
- ./api/dashboard/... codegen path from hack/update-codegen.sh

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-05-20 16:33:21 +02:00
Myasnikov Daniil e5437240e7 fix(opensearch-operator): use values.yaml override instead of patch
Move the kube-rbac-proxy image override from a patch + vendored-chart
edit to a values.yaml override under kubeRbacProxy.image.repository.

This matches the project convention documented in docs/agents/overview.md
("Override configuration in root values.yaml") and the pattern used by
sibling packages such as kamaji.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-20 15:45:08 +05:00
Myasnikov Daniil d0a0b06afd fix(opensearch-operator): replace deprecated kube-rbac-proxy image
The gcr.io/kubebuilder/kube-rbac-proxy image is no longer available
since GCR was deprecated. Replace it with quay.io/brancz/kube-rbac-proxy
from the original upstream author.

Applied as a local patch in patches/ because upstream chart 2.8.1+ drops
kubeRbacProxy entirely but also bumps CRDs to controller-gen v0.19.0 with
significant schema changes — out of scope for this fix.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-20 15:45:08 +05:00
Arsolitt 69a479df5a fix(kafka): reclaim ZooKeeper PVCs on Kafka release deletion
Set deleteClaim: true on the ZooKeeper persistent-claim storage so the
Strimzi operator removes the PVCs together with the rest of the cluster
when the Kafka CR is deleted. Previously the Kafka broker PVCs were
reclaimed (deleteClaim: true) but the ZooKeeper PVCs were orphaned,
forcing manual cleanup before the same release name could be reused.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-05-20 13:45:07 +03:00
Myasnikov DaniilandClaude Opus 4.7 45937d6d13 fix(e2e-sandbox): use curl -fsSL when downloading mc
The minio client was downloaded without -L, so when dl.min.io served
a 302 redirect, curl wrote the HTML body ("<a href=...>Found</a>") to
/usr/local/bin/mc, chmod +x'd it, and shipped that as the binary. When
the bucket E2E test ran "mc alias set ...", the shell tried to interpret
the HTML as a script and errored with:

    /usr/local/bin/mc: 1: cannot open a: No such file

(dash reading "<a" as an input redirect from a file named "a").

The image is rebuilt per CI run from cached layers; the broken mc layer
was cached and reused. Adding -fsSL fixes the download and the textual
change busts the cache so the next build pulls a real ELF binary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-20 10:40:32 +05:00
myasnikovdaniil 3c1c848cb3 fix(platform): add OpenSearch to PaaS bundle (#2648)
## What this PR does

OpenSearch has shipped as a complete package set in the repo for several
releases — `packages/apps/opensearch/`,
`packages/system/opensearch-operator/`,
`packages/system/opensearch-rd/`, and `PackageSource` files under
`packages/core/platform/sources/` — but the PaaS bundle template was
never
updated to include the two `cozystack.opensearch-*` PackageSources.

Result on any cluster with `bundles.paas.enabled=true`:
- `opensearch-operator` is not deployed → no `OpenSearchCluster` CRD on
cluster
- `opensearch-rd` release is not deployed → no
`ApplicationDefinition/opensearch` → the dashboard catalog has no
OpenSearch entry and tenants cannot create
`opensearches.apps.cozystack.io`

This PR adds the missing includes alongside every other DB
operator/application in
`packages/core/platform/templates/bundles/paas.yaml`.

### Release note
```release-note
Fix: OpenSearch is now installed by the PaaS bundle (`bundles.paas.enabled=true`); previously the operator and ApplicationDefinition were defined but never referenced from the bundle, so OpenSearch did not appear in the dashboard catalog.
```

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

## Summary by CodeRabbit

* **New Features**
* OpenSearch operator and application packages are now included in the
PaaS bundle when enabled, providing additional search and analytics
capabilities for supported deployments.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2648)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-19 15:27:38 +05:00
Andrei Kvapil e84677782c revert(ingress)!: remove broken publishing.exposure=loadBalancer mode (#2603)
## What this PR does

Reverts the `publishing.exposure=loadBalancer` mode for ingress-nginx
added in #2468.

The mode rendered the ingress-nginx Service as `type: LoadBalancer` and
a `CiliumLoadBalancerIPPool` over `publishing.externalIPs`, as a
migration path off `Service.spec.externalIPs` (deprecated upstream in
Kubernetes v1.36 per KEP-5707). In default Cozystack the implementation
produces two failure modes:

- `publishing.externalIPs` is the operator-supplied list of node public
IPs (addresses the cluster nodes already hold and listen on, see [docs
section "4b. Node Public IP
Setup"](https://cozystack.io/docs/v1.3/install/cozystack/platform/#4b-node-public-ip-setup)),
not a virtual-VIP pool. Treating it as one and DNATing those IPs via
Cilium LB-IPAM collides with the existing ingress-nginx DNAT on the same
IPs (`Service.spec.externalIPs` path), so traffic is forwarded
non-deterministically.
- Cilium in default Cozystack runs with `ipam.mode: kubernetes` and no
L2/BGP announcer, so a pool over addresses outside the node range is
allocated but never advertised — the Service stays unreachable.

The KEP-5707 deprecation is real, but the right migration is a
mechanism-agnostic tenant API knob translated by a Cozystack-side
controller into whichever backend the cluster admin has configured
(MetalLB pool, Cilium LB-IPAM, robotlb, externalIPs pin) — not a
chart-side `IPPool` baked into the tenant chart. That follow-up belongs
in its own PR.

### Removed

- `publishing.exposure` value (and the inline comment) from
`core/platform/values.yaml`.
- `expose-mode` plumbing line in `core/platform/templates/apps.yaml`.
- `CiliumLoadBalancerIPPool` template at
`apps/tenant/templates/cilium-lb-pool.yaml`.
- `type: LoadBalancer + externalTrafficPolicy: Local` branch in
`extra/ingress/templates/nginx-ingress.yaml`. The Service now renders as
`ClusterIP + spec.externalIPs` when externalIPs are set, falling through
to `type: LoadBalancer` when they are not — pre-#2468 shape.
- Exposure mode section from `extra/ingress/README.md`.
- `exposure_test.yaml` fixtures in both charts.

### Compatibility

Operators who explicitly set `publishing.exposure=loadBalancer` will see
their ingress Service revert to `ClusterIP + spec.externalIPs` on
upgrade. Anyone on the default value (`externalIPs`) is unaffected.

### Release note

```release-note
revert(ingress): remove the publishing.exposure=loadBalancer mode added in #2468. The default externalIPs path is unchanged; operators who explicitly opted into loadBalancer will have their ingress Service revert to ClusterIP + spec.externalIPs on upgrade. Migration off Service.spec.externalIPs (deprecated in KEP-5707) will be reintroduced via a mechanism-agnostic API knob in a follow-up.
```

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

## Release Notes

* **Refactor**
* Removed conditional generation of the Cilium load‑balancer IP pool and
simplified service exposure to rely on explicit external IP values.
  * Streamlined configuration for service exposure keys.

* **Documentation**
  * Removed the exposure-mode guidance from ingress documentation.

* **Tests**
* Deleted the exposure-mode-related test suites covering various
external IP and loadBalancer scenarios.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2603)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-19 10:47:45 +02:00
Andrey Kolkov 15c015c226 fix(platform): fix broken dashboard login by token
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-15 12:11:17 +04:00
Myasnikov Daniil 999202c004 fix(platform): add OpenSearch to PaaS bundle
The opensearch chart, operator chart, ApplicationDefinition and
PackageSources have existed since the original OpenSearch series, but the
PaaS bundle template never references the opensearch-operator or
opensearch-application PackageSources. On isp-* variants with
bundles.paas.enabled=true, neither the operator nor the ApplicationDefinition
is ever installed, so the dashboard catalog has no OpenSearch entry and
opensearches.apps.cozystack.io resources cannot be created.

Add both includes to packages/core/platform/templates/bundles/paas.yaml,
following the same pattern used for every other DB application.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-14 10:28:15 +05:00
cozystack-ci[bot] 9f655e29ff Prepare release v1.4.0-rc.2
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-05-13 21:48:39 +00:00
myasnikovdaniil 680803acae feat(dashboard): replace UI with new cozystack-ui build (#2507)
## Summary

Replaces the old openapi-ui with the new cozystack-ui build from the
cozystack-ui repository.

## Key Changes

### New UI
- **Image**: `999669/cozystack-ui:latest@sha256:ea4e832c2...`
- **Architecture**: `linux/amd64`
- **Repository**: https://github.com/cozystack/cozystack-ui
(feat/ui-overhaul branch)

### Removed Components
- Removed BFF (Backend-For-Frontend) container - new UI communicates
directly with Kubernetes API
- Removed old openapi-ui and openapi-ui-k8s-bff build targets from
Makefile
- Removed old UI Dockerfiles and patch files

### RBAC Updates
- Added read permissions for `cozystack.io/applicationdefinitions` to
fix 403 errors
- New UI requires access to applicationdefinitions for marketplace and
catalog functionality

### Bug Fixes
- Fixed VNC WebSocket connections to use dynamic URLs instead of
hardcoded localhost:8001
- VNC now works properly in-cluster deployments

## Testing

Tested on dev3.infra.aenix.org cluster:
- ✅ Dashboard loads correctly
- ✅ Application definitions accessible
- ✅ VNC connections working
- ✅ Backup pages functional
- ✅ All TypeScript errors resolved

## Migration Notes

The new UI is a complete rewrite with:
- Modern React 19 + TypeScript stack
- Improved backup management pages
- Better type safety and error handling
- Direct Kubernetes API communication (no BFF needed)
2026-05-13 22:54:15 +05:00
Andrey Kolkov c5b46f31fe fix(platform): align field of Altinity backup-strategy for similarity… (#2645)
… with others

<!-- 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 `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### 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. -->

### 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
align Altinity backup strategy field name for similarity with others
```
2026-05-13 20:17:19 +04:00
myasnikovdaniil 42b5cc4fd6 Merge branch 'main' into feat/new-ui
Signed-off-by: myasnikovdaniil <60174387+myasnikovdaniil@users.noreply.github.com>
2026-05-13 20:19:47 +05:00
Andrey Kolkov 84874353ce fix(platform): align field of Altinity backup-strategy for similarity with others
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-13 17:15:51 +04:00
cozystack-ci[bot] 252c18f3e8 Prepare release v1.4.0-rc.1
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-05-13 12:29:01 +00:00
Andrey Kolkov 92930ad1aa feat(platform): add implementation of backup-strategy controller for foundationdb
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-13 09:49:19 +04:00
Andrey Kolkov 5f83ae72a8 feat(platform): implement backup strategy for mariadb (#2605)
<!-- 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 `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### 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. -->

### 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
implement backup strategy for MariaDB
```

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

* **New Features**
* Added native MariaDB backup/restore strategy using the operator-native
BackupClass/Plan/RestoreJob flow with example manifests for end-to-end
workflows.

* **Documentation**
* Added MariaDB how‑to and examples; marked legacy restic/chart-side
backup parameters as DEPRECATED and documented migration guidance.

* **Tests**
* Added unit tests covering MariaDB backup/restore reconciliation,
templating, storage handling, idempotency, URIs, and deadlines.

* **Chores**
* Extended controller RBAC to allow observing MariaDB operator
resources.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2605)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-12 17:10:55 +04:00
Andrei Kvapil f38a047aa6 feat(kilo): add patched image build with cross mesh granularity (#2604)
## What this PR does

Adds infrastructure for building a patched kilo image from upstream
`main` branch source, following the same pattern as the kamaji package.

The patch introduces a new `cross` mesh granularity mode — WireGuard
tunnels are created only between nodes in different locations, while
intra-location traffic stays on the CNI overlay.

Changes:
- `images/kilo/Dockerfile` — multi-stage build: fetches upstream
tarball, applies patches, builds `kg`/`kgctl` binaries with CNI plugins
and iptables-wrapper
- `images/kilo/patches/feat-add-cross-mesh-granularity.diff` — adds
`CrossGranularity` to the kilo binary
- `Makefile` — new `image:` target for building the patched image,
updated `update:` target to pin upstream commit SHA

### Screenshots

N/A — no UI changes.

### Release note

```release-note
feat(kilo): add patched image build with cross mesh granularity support
```

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

* **New Features**
* Added "cross" mesh granularity mode for finer topology grouping,
adjusted routing/encryption behavior
* Added e2e tests covering cross-mode scenarios for improved reliability

* **Chores**
* Build automation now tracks latest main commit and derives image
digests from build metadata
* Introduced multi-stage container build and runtime packaging for kilo,
including required networking tooling
* **Configuration**
  * Updated default image reference to a new repository and digest

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2604)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-12 12:38:53 +02:00
Andrei Kvapil 92d128e8ff feat(keycloak): support extraEnv values and userProfile customization (#2613)
## What this PR does

Two related quality-of-life additions to the Keycloak system component:

1. **`keycloak` chart now accepts `extraEnv`.** Operators can append
extra
`EnvVar` entries to the Keycloak StatefulSet container without forking
the
   chart. Useful when a custom theme needs runtime configuration via
`kcContext.properties`, or when an extension expects environment
variables.

2. **`keycloak-configure` chart now exposes a top-level `userProfile`
value**
that is mirrored verbatim into
`ClusterKeycloakRealm.spec.userProfileConfig`.
   This lets operators declare custom user-profile attributes (required
fields, validators, permissions, ...) declaratively, alongside the
existing
   `login` and `smtp` blocks.

Both values default to empty, so existing deployments are unaffected.

### Release note

```release-note
feat(keycloak): support extraEnv values and userProfile customization
```
2026-05-12 12:38:35 +02:00
Andrey Kolkov 62605a198b feat(platform): implement backup strategy for mariadb
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-12 13:56:29 +04:00
Kirill Ilin 3e5b370f74 chore(keycloak): use generic example values in chart comments
Replace deployment-specific examples in the new extraEnv and
userProfile docs with generic placeholders so the chart comments do
not lean on any particular downstream use case.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-05-12 13:13:19 +05:00
Kirill Ilin 2261ef7e25 feat(keycloak-configure): plumb userProfile into ClusterKeycloakRealm
Operators can now declare custom user-profile attributes (required
fields, validators, permissions, ...) via a new top-level userProfile
value. The value is mirrored verbatim into the
ClusterKeycloakRealm.spec.userProfileConfig managed by the EDP
keycloak-operator.

The default remains empty, so realms that don't need custom user
profile configuration are unaffected.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-05-12 13:09:29 +05:00
Kirill Ilin 6413052abc feat(keycloak): expose extraEnv values to the StatefulSet
Custom themes (Keycloakify, FTL) often need runtime values from the
deployment environment via kcContext.properties. Adding an extraEnv
list to the chart lets operators inject those values per cluster
without forking the chart.

The new field appends verbatim to the container env list and accepts
the full corev1.EnvVar schema, including valueFrom for secret or
configMap-backed values.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-05-12 13:09:21 +05:00
Aleksei Sviridkin 55a98b8815 docs(apps): point seven app README preset tables at canonical docs
Address review feedback from coderabbitai across:
  - packages/apps/kubernetes/README.md
  - packages/apps/mariadb/README.md
  - packages/apps/nats/README.md
  - packages/apps/qdrant/README.md
  - packages/apps/redis/README.md
  - packages/apps/tcp-balancer/README.md
  - packages/apps/vpn/README.md

Each of these READMEs left an inline 7-row preset table listing only
the legacy flat names after the parameters block above already
defaulted to an instance-type value (e.g. `t1.nano`). cozyvalues-gen
only regenerates the `## Parameters` block, so the hand-written
section drifted.

Replace the inline tables with the same short paragraph already used
by postgres and kafka READMEs: name the five series, note that the
seven legacy names remain accepted as deprecated aliases, and link to
the canonical reference at docs/operations/resource-presets.md so the
full size matrix and legacy mapping live in exactly one place.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-12 03:08:52 +03:00
Aleksei SviridkinandMatthieu ROBIN cca54d876a docs(operations): resource presets reference
Document the five instance-type series (t1/c1/s1/u1/m1) introduced
alongside the legacy preset deprecation, the per-size CPU and memory
matrix, and the 1:1 legacy-to-instance-type mapping table that powers
cozy-lib aliases, migration 39, and the operator-side deprecation
warning.

Single source of truth referenced from:
  - packages/library/cozy-lib/templates/_resourcepresets.tpl
  - packages/core/platform/images/migrations/migrations/39
  - pkg/apis/apps/presets/legacy.go

Includes an explicit callout that legacy `medium` (1 CPU / 1Gi) and
instance-type `*.medium` (2 CPU) are not the same — same word,
different resources.

Replace inline preset tables in packages/apps/postgres/README.md and
packages/apps/kafka/README.md with a short paragraph that names the
five series and points at the canonical doc, since the per-app
inline tables listed only the legacy seven and would drift further
as the canonical matrix evolves.

Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 21:10:19 +03:00
Aleksei SviridkinandMatthieu ROBIN 55850aa3d8 test(apps): backward-compat helm-unittest and CI invariants
Pin the 1:1 equivalence between legacy flat preset names and their
instance-type counterparts in helm-unittest for three charts that
exercise different preset shapes:

  - postgres   — top-level only
  - clickhouse — top + nested clickhouseKeeper preset
  - opensearch — top + nested dashboards preset

Each case asserts that nano <-> t1.nano, small <-> t1.small,
medium <-> c1.small, large <-> c1.medium, and micro <-> t1.micro
render the same CPU and memory.

packages/apps/{clickhouse,opensearch}/Makefile gain a `test:` target
that calls helm unittest so hack/helm-unit-tests.sh actually runs the
new suites in CI; without it the suites would land as dead weight.

hack/check-rd-presets.sh enforces two invariants in `make unit-tests`:

  - every packages/system/*-rd/cozyrds/*.yaml openAPISchema carries
    the canonical 47-preset enum (40 instance-type + 7 legacy);
  - no chart template carries a hardcoded legacy preset literal in
    a defaultingSanitize (list "<legacy>" ...) form.

Both turn an accidental drop of a per-chart Makefile step or an
accidental legacy hardcode into a CI failure rather than silent
production-time validation rejection.

Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 21:09:50 +03:00
Aleksei SviridkinandMatthieu ROBIN f18ce1974e feat(platform): migration 39 converts legacy resourcesPreset values
Add a new pre-upgrade hook migration that walks every HelmRelease
spec.values and every app CR spec under apps.cozystack.io/v1alpha1, and
rewrites legacy flat resourcesPreset values (nano/micro/small/medium/
large/xlarge/2xlarge) to their instance-type equivalents (t1/c1).

Mapping is 1:1 by CPU and memory — the migration never bumps resource
usage. The same table lives in cozy-lib (legacy aliases) and in
pkg/apis/apps/presets/legacy.go.

Properties:

  - Idempotent: re-running on a converted cluster is a no-op because
    non-legacy values fall through the jq map_legacy else arm.
  - Best-effort: a single failed patch logs a warning and continues;
    cozy-lib legacy aliases keep charts rendering with identical CPU
    and memory even if the migration is incomplete. kubectl patch
    stderr is captured and surfaced in the WARN line so operators see
    why a patch was rejected.
  - Distinguishes "no items" from "kubectl errored" (RBAC stripped,
    network down, CRD missing) so a broken cluster cannot look
    identical to a clean no-op run and stamp the version
    unconditionally.
  - Dry-run: MIGRATION_DRY_RUN=1 prints the would-be patches and
    leaves the version stamp at 39.
  - Final summary echoed to both stdout and stderr.
  - Version stamp only advances to 40 on a fully successful real run.

Bump migrations.targetVersion 39 -> 40 so the hook executes on the
next platform upgrade.

Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 21:09:24 +03:00
Aleksei SviridkinandMatthieu ROBIN fdffedbd4b feat(apps): instance-type resourcesPreset across apps and extras
Rewrite resourcesPreset values in every apps/* and extras/* chart from
the legacy flat names to the new instance-type form, using the 1:1
mapping:

  nano    -> t1.nano       micro   -> t1.micro
  small   -> t1.small      medium  -> c1.small
  large   -> c1.medium     xlarge  -> c1.large
  2xlarge -> c1.xlarge

Each affected values.yaml carries the canonical 47-value enum (40
instance-type names plus 7 legacy aliases) so the schema accepts both
forms during the deprecation window. cozyvalues-gen regenerates the
matching api/apps/v1alpha1/*/types.go kubebuilder enum and defaults,
the chart values.schema.json, the README parameter table, and the
ApplicationDefinition openAPISchema in packages/system/*-rd/cozyrds.

Three hardcoded legacy literals in chart templates migrated:
  - packages/apps/harbor/templates/harbor.yaml:121 (portal)
  - packages/apps/opensearch/templates/opensearch.yaml:43 (dashboards)
  - packages/system/keycloak/templates/sts.yaml:115 (heap fallback)

packages/apps/foundationdb/Makefile gains the missing hack/update-crd.sh
call. Without it, every regen left foundationdb-rd cozyrds stale and
new FoundationDB app CRs through cozystack-api were rejected at
validation for any non-legacy resourcesPreset value.

Every default-rendered chart produces identical CPU and memory to the
value applied before the rename, courtesy of the legacy alias table in
cozy-lib and the 1:1 default mapping.

Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 21:08:41 +03:00
Aleksei SviridkinandMatthieu ROBIN af86c9d5b8 feat(cozy-lib): instance-type preset names with five series and legacy aliases
Restructure the resource preset table into a flat <series>.<size> naming
convention inspired by KubeVirt and cloud-provider instance types. Five
series cover the full CPU-to-memory ratio range:

  t1 (Tiny,      1:0.5) — burstable, low memory
  c1 (Compute,   1:1)   — CPU-balanced, CPU-bound workloads
  s1 (Standard,  1:2)   — proxies, caches, lightweight services
  u1 (Universal, 1:4)   — databases and messaging
  m1 (Memory,    1:8)   — search and analytics

Each series provides eight sizes (nano, micro, small, medium, large,
xlarge, 2xlarge, 4xlarge), yielding 40 presets in total. Ephemeral
storage remains 2Gi for every preset.

The seven legacy flat names (nano, micro, small, medium, large, xlarge,
2xlarge) are retained as backward-compatibility aliases returning their
original CPU and memory values. Existing HelmReleases and app CRs
continue to render identical resources without a migration step. The
legacy block is deprecated and will be removed in a future release; the
1:1 conversion table is:

  nano    -> t1.nano       (250m / 128Mi)
  micro   -> t1.micro      (500m / 256Mi)
  small   -> t1.small      (1    / 512Mi)
  medium  -> c1.small      (1    / 1Gi)
  large   -> c1.medium     (2    / 2Gi)
  xlarge  -> c1.large      (4    / 4Gi)
  2xlarge -> c1.xlarge     (8    / 8Gi)

Also fix the preset helper docstring to pass the preset string straight
to unsanitizedPreset, replacing the prior list applied-to-tuple form
that silently invoked the dot value as a function.

Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 21:08:13 +03:00
Arsolitt 0be25ce59e build(kilo): pin patched image to cozystack registry
Point values.yaml to ghcr.io/cozystack/cozystack/kilo with the
freshly built image digest, replacing the upstream ghcr.io/squat/kilo
reference.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-05-11 20:33:28 +03:00
Arsolitt e9c3817f4f fix(kilo): remove stale Alpine repo override in runtime stage
Address review feedback from gemini-code-assist on Dockerfile:36:
Remove custom Fastly CDN repo pinned to v3.20 while base image is
alpine:3.23 — use default repos from the base image instead.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-05-11 16:42:31 +03:00
Andrey Kolkov 250e1b3376 feat(platform): implement backup strategy for clickhouse application
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-11 14:46:48 +04:00
Arsolitt 269fe97e34 feat(kilo): add patched image build infrastructure
Add Dockerfile for building kilo from upstream main branch source with
patch support, CNI plugins, and iptables-wrapper. Update Makefile with
`image:` target for building the patched image and an updated `update:`
target that pins the upstream commit SHA.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-05-11 12:11:31 +03:00
Andrei KvapilandClaude f919afe15f revert(ingress)!: remove broken publishing.exposure=loadBalancer mode
Reverts the ingress loadBalancer exposure mode added in #2468.

The mode rendered the ingress-nginx Service as type: LoadBalancer and a
CiliumLoadBalancerIPPool over publishing.externalIPs, as a migration path
off Service.spec.externalIPs (deprecated upstream in Kubernetes v1.36 per
KEP-5707). In default Cozystack the implementation produces two failure
modes:

- publishing.externalIPs is the operator-supplied list of node public IPs
  (addresses the cluster nodes already hold and listen on), not a
  virtual-VIP pool. Treating it as one and DNATing those IPs via Cilium
  LB-IPAM collides with the existing ingress-nginx DNAT on the same IPs
  (Service.spec.externalIPs path), so traffic is forwarded
  non-deterministically.
- Cilium in default Cozystack runs with ipam.mode: kubernetes and no
  L2/BGP announcer, so a pool over addresses outside the node range is
  allocated but never advertised — the Service stays unreachable.

The KEP-5707 deprecation is real, but the right migration is a
mechanism-agnostic tenant API knob translated by a Cozystack-side
controller into whichever backend the cluster admin has configured
(MetalLB pool, Cilium LB-IPAM, robotlb, externalIPs pin) — not a
chart-side IPPool baked into the tenant chart. That follow-up belongs in
its own PR.

Removed:
- publishing.exposure value (and the inline comment) from
  core/platform/values.yaml
- expose-mode plumbing line in core/platform/templates/apps.yaml
- CiliumLoadBalancerIPPool template at
  apps/tenant/templates/cilium-lb-pool.yaml
- type: LoadBalancer + externalTrafficPolicy: Local branch in
  extra/ingress/templates/nginx-ingress.yaml (Service renders back as
  ClusterIP + spec.externalIPs when externalIPs are set, falling through
  to type: LoadBalancer when they are not — pre-#2468 shape)
- Exposure mode section from extra/ingress/README.md
- exposure_test.yaml fixtures in both charts

Operators who explicitly set publishing.exposure=loadBalancer will see
their ingress Service revert to ClusterIP + spec.externalIPs on upgrade.
Anyone on the default value (externalIPs) is unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-05-11 10:42:29 +02:00
Aleksei Sviridkin 6f83d9a794 feat(ouroboros): bump to v0.7.2 with debug logging on tenants
The v0.7.2 ouroboros release adds --log-level / OUROBOROS_CONTROLLER_LOG_LEVEL,
plants Debug logs at every informer event handler (AddFunc/UpdateFunc/
DeleteFunc with kind+namespace/name via describeObj) and at reconcile
entry/exit, wires SetWatchErrorHandler on Ingress / Gateway / HTTPRoute
informers, and surfaces cache-sync elapsed at Info. The diagnostic
surface targets the kamaji-tenant-via-konnectivity flake where bats
sees a fresh Ingress create succeed against the tenant apiserver but
the controller's coredns-custom rewrite lands roughly 7-10 minutes
later — without per-event visibility the failure mode is
indistinguishable between 'watch never fires', 'reconcile silently
errors', and 'API list returns empty until reflector reconnect'.

Bumps the chart-manifest digest, the controller-image digest, and the
cozystack-side image-pin assertion in lockstep. The bats deadline for
the coredns-custom rewrite check rises from 180s to 900s — even on
the broken-watch path the reflector eventually reconnects within
that budget, so the test stops failing on the symptom while we keep
the diagnostic on to learn what the symptom actually is.

cozystack tenants force controller.logLevel: debug via the
defaultOuroborosValues helper; operators that want quieter logs can
override through addons.ouroboros.valuesOverride. Two helm-unittest
fixtures pin both the default and the override path.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 02:20:34 +03:00
Aleksei Sviridkin d6c24dd078 fix(ouroboros): pin tenant clusterDomain to cluster.local
The cozystack tenant ouroboros wrapper deliberately left
`controller.clusterDomain` empty so the upstream chart's per-pod
`/etc/resolv.conf` auto-detect could pick up whatever the tenant CoreDNS
actually serves. In a cozystack tenant that auto-detect resolves to the
host platform's `cozy.local` rather than the tenant's `cluster.local`:
the cozystack tenant kubelet injects the host's cluster-domain into pod
search lists, but Kamaji's TenantControlPlane is configured with
`networkProfile.clusterDomain: cluster.local` and the tenant CoreDNS
Corefile serves only that domain. The mismatch makes the proxy compose
its `BackendHost` as
`ingress-nginx-controller.cozy-ingress-nginx.svc.cozy.local`, which
NXDOMAINs against tenant CoreDNS — `/readyz` returns 503 forever, the
proxy Deployment never reaches Ready, helm install times out at 10
minutes, and the e2e job hits the 2h cancel without `ouroboros.bats`
ever progressing past the first kubectl wait.

Pin `controller.clusterDomain: cluster.local` in `defaultOuroborosValues`
so the chart bakes the resolved BackendHost against the domain tenant
CoreDNS actually serves. This is the contract Kamaji TCP defines for
every cozystack tenant; operators on a non-default tenant CoreDNS can
still override via
`addons.ouroboros.valuesOverride.ouroboros.controller.clusterDomain`.

The underlying tenant kubelet injecting the wrong cluster-domain into
pod resolv.conf is a separate bootstrap concern that should be addressed
on its own track; pinning the chart input here keeps ouroboros
operational on the default cozystack tenant topology in the meantime.

Update the corresponding helm-unittest to assert the new contract.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-08 10:49:51 +03:00
Andrei Kvapil 99e7070e4e fix(seaweedfs): wait for ingress-nginx-system before creating Ingress (#2406)
## What this PR does

Stacks on top of #2258. Adds `dependsOn: ingress-nginx-system` to the
inner `seaweedfs-system` HelmRelease so flux blocks its install until
the ingress chart is Ready.

## Why

In #2258's E2E run (and the `cozyreport-2258` bundle from this morning),
`seaweedfs-system` failed to install with:

```
Helm install failed for release tenant-root/seaweedfs-system: 1 error occurred:
* Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io":
  failed to call webhook: Post "https://root-ingress-controller-admission.tenant-root.svc:443/networking/v1/ingresses?timeout=10s":
  dial tcp 10.96.33.42:443: connect: operation not permitted
```

### Root cause class

The outer tenant HelmReleases (`apps/tenant/templates/*.yaml`) render
one inner HR per subsystem and let flux reconcile them. Both `seaweedfs`
and `ingress` outer HRs get applied in parallel, and each of them spawns
its own inner HR — which flux then installs in parallel too.

When `seaweedfs-system` gets to the `creating 39 resource(s)` step, it
tries to create the tenant Ingress. The apiserver calls the
ingress-nginx `ValidatingWebhookConfiguration` (which uses
`failurePolicy: Fail` by default). If the nginx controller
pod/Service/endpoint state hasn't finished reconciling on the CNI yet,
the connect to the ClusterIP fails with `EPERM`, the webhook errors,
Helm aborts, and flux rolls back.

E2E recovers via the `flux reconcile --force` retry loop in
`hack/e2e-install-cozystack.bats`, but it's flaky and adds minutes.

### Fix

Introduce an explicit ordering dependency at the inner HR layer:

```yaml
{{- if $ingress }}
dependsOn:
- name: ingress-nginx-system
{{- end }}
```

Flux waits for `ingress-nginx-system` to reach `Ready=true` before
installing seaweedfs — which, under Flux's wait semantics, means the
ingress-nginx Deployment is Available and its webhook endpoint is
routable. The race disappears.

Guarded by `{{- if $ingress }}` so this only kicks in for tenants that
actually have ingress enabled.

## Test plan

- [ ] Rerun E2E on #2258 — `seaweedfs-system` should install on the
first try without a `Retry install` iteration.
- [ ] Verify rendered template still matches expectations when
`$ingress` is unset (no `dependsOn` block).

### Release note

```release-note
[seaweedfs] Make tenant seaweedfs wait for ingress-nginx to be Ready before creating its Ingress resource, avoiding a webhook race that intermittently failed install on fresh clusters.
```

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

* **Bug Fixes**
* Improved deployment sequencing so the system now waits for the Ingress
controller to be ready before creating or updating resources that
include Ingress. This prevents premature deployments and related
failures, reduces rollout errors, and provides more reliable,
predictable deployments for applications relying on Ingress resources.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-07 18:42:08 +02:00