Add the storageclass dynamic-dropdown x-cozystack-options annotation to the
storageClass field of the data apps (openbao, clickhouse, mariadb, redis,
harbor, opensearch, rabbitmq, mongodb, postgres, nats, kafka, qdrant,
foundationdb, http-cache) at the values.yaml source, so the extension is
emitted by cozyvalues-gen instead of hand-edited into values.schema.json.
kafka carries two (Kafka and ZooKeeper structs); foundationdb is nested
under the Storage struct.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
Mark fields that reference cluster resources with the x-cozystack-options
vendor keyword so the dashboard renders them as dynamic dropdowns:
GPU, instanceType, instanceProfile, network (vm-instance), GPU/
instanceType (kubernetes nodeGroups), image (vm-disk), storagePool
(bucket), storageClass (all apps) and disks[].name (vm-instance). Drop
the stale static instanceProfile enum.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
Annotate storageClass as immutable across every stateful app whose
chart exposes one — clickhouse, foundationdb, harbor, http-cache,
kafka (Kafka + ZooKeeper), kubernetes (top-level only), mariadb,
mongodb, nats, openbao, opensearch, postgres, qdrant, rabbitmq,
redis, vm-disk. Without coverage across the catalogue the UI would
render storageClass read-only on some apps and editable on others —
a worse mental model than uniform behaviour.
The annotation lands at the schema layer (CEL XValidation rule
'self == oldSelf' on values.schema.json / types.go / embedded
openAPISchema in cozyrds/<app>.yaml). The cozystack aggregated
apiserver does not yet evaluate CEL on application-backed resources;
that is tracked at cozystack/cozystack#2657. The UI
(cozystack/cozystack-ui#6) consumes the marker today.
The PVC-pinning argument is identical for every chart: Kubernetes
fixes a PVC's storageClassName at creation time, so editing this
field on an existing resource never migrated data. The schema
annotation makes that contract explicit.
kubernetes.nodeGroups[name].storageClass is intentionally NOT
annotated: the field is optional and undefaulted, so a strict
'self == oldSelf' rule would block any future attempt to set it on
an existing node group. A richer ratcheting form is needed there —
see docs/storage-immutability.md.
A refactor in pkg/registry/apps/application/rest.go extracts the
inline schema-build path into buildSpecSchema so it can be
unit-tested. Three regression tests in rest_spec_schema_test.go pin
the contract that x-kubernetes-validations survives the
v1->internal->structural conversion (loaded from the actual MariaDB
ApplicationDefinition fixture on disk, not a hand-edited mock).
Each per-app README gains a one-line blockquote pointing to a new
docs/storage-immutability.md that explains the contract and which
consumers enforce it today.
Requires cozystack/cozyvalues-gen with @immutable support — bump the
pre-commit pin in .github/workflows/pre-commit.yml once the release
exists.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
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>
## What this PR does
Fixes#2514.
When `external: true` is set on a non-sharded `MongoDB`, Cozystack
rendered a single
`LoadBalancer` Service whose selector matched **all** replica-set
members
(`app.kubernetes.io/replset: rs0`). Round-robin balancing sent ~⅔ of
writes to
secondaries and they were rejected with `MongoServerError: not primary`.
Drivers couldn't recover on their own either: connecting through the LB
returned
the in-cluster DNS names of all members from `isMaster`, none of which
resolve
from outside the cluster, so automatic primary discovery failed.
This PR replaces the manual `external-svc.yaml` (for replica-set mode)
with the
Percona MongoDB operator's native `replsets[].expose` feature:
```yaml
replsets:
- name: rs0
expose:
enabled: true
type: LoadBalancer
externalTrafficPolicy: Local
```
The operator then:
- creates one `LoadBalancer` Service **per pod** (`<release>-rs0-0`,
`-1`, `-2`)
with `statefulset.kubernetes.io/pod-name` selectors,
- watches `Service.status.loadBalancer.ingress` for each pod and
rewrites the
member `host` in `rs.conf` to the external IPs.
External clients use a standard replica-set URI
(`mongodb://...:27017,...:27017,...:27017/?replicaSet=rs0`) and the
driver
discovers the primary correctly from outside the cluster.
For **sharded** mode the existing single `LoadBalancer` is preserved —
it
front-ends stateless `mongos`, which can route writes to the right shard
primary itself. `external-svc.yaml` is now scoped to that case only.
### Tested
- `helm unittest .` in `packages/apps/mongodb/`: 91/91 pass.
- Applied on a dev stand:
- `replsets[0].expose` is set as expected in the rendered PSMDB CR.
- The Percona operator (`v1.21.1`) accepts the spec and creates 3
per-pod
`LoadBalancer` Services with `statefulset.kubernetes.io/pod-name`
selectors. The previous single round-robin Service is no longer
rendered.
- With an LB IP pool present, the operator promotes the allocated LB IPs
into
the PSMDB `status.host` field and into `rs.conf`.
End-to-end mongosh write loop wasn't run on the dev stand because the
underlying network does not advertise/route allocated LoadBalancer IPs
(no
working L2/BGP path), so the rewritten `rs.conf` members are unreachable
in
that environment. The structural change is otherwise fully verified and
the
issue's reproduction scenario expects a real LB-capable network.
### Release note
```release-note
fix(mongodb): expose each replica-set member through its own LoadBalancer when `external: true`, so external clients can discover the primary via the standard replica-set connection string and writes no longer fail with "not primary" errors.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* External LoadBalancer services now only render when both external and
sharding are enabled.
* Replica set external exposure is now conditional based on the external
setting.
* Service configuration now uses LoadBalancer type with Local traffic
policy for external access.
* Service selectors refined for proper mongos targeting in sharded
deployments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Describe the in-place password rotation performed by the Percona operator
when upgrading to releases that scope the system users secret per CR.
Clarify that consumers of the credentials secret need a pod restart to
pick up the new password, custom user secrets are unaffected, and the
legacy namespace-shared secret is left orphaned and must be cleaned up
manually.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Update Data lifecycle section to address review feedback:
- Reflect the release-scoped users secret naming.
- Distinguish operator-driven PVC release from actual PV/data
deletion (depends on StorageClass reclaimPolicy).
- Document that TLS secrets (<release>-ssl, <release>-ssl-internal)
are not reclaimed automatically.
- Add a recovery note for the case where the operator is removed
before the CR, leaving finalizers unable to run.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Test descriptions said 'percona.com/delete-psmdb-pvc finalizer'
while the assertions verified both delete-psmdb-pvc and
delete-psmdb-pods-in-order. Use 'Percona finalizers' to match
what the tests actually check.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
The operator deletes spec.secrets.users (defaulted to the global
percona-server-mongodb-users) as part of the delete-psmdb-pvc
finalizer flow. With the default value, deleting one MongoDB
release in a namespace would remove the secret shared with any
other MongoDB release there.
Override spec.secrets.users with a release-scoped name so each
release owns its own secret and uninstalls do not collide.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
The previous wording listed only one operator-managed secret with
'e.g.' prefix. The Percona Operator finalizer flow actually deletes
four specific secrets (internal-<release>, internal-<release>-users,
<release>-mongodb-encryption-key, and the global default
percona-server-mongodb-users). Document each by name so operators
understand the full deletion blast radius, especially the global
default which can collide with externally-created secrets sharing
the same name.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Distinguish between operator-managed secrets reclaimed via the
finalizer flow (e.g. internal-*-users) and Helm-managed secrets
(credentials, user secrets, s3 backup creds) removed by helm
uninstall. The previous wording was ambiguous and could mislead
users about which cleanup path applies to which secret.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
PerconaServerMongoDB CR now carries the upstream-default finalizer
pair: percona.com/delete-psmdb-pods-in-order ensures primary-last
graceful shutdown before percona.com/delete-psmdb-pvc reclaims
storage. This matches deploy/cr.yaml defaults in operator v1.21.1
and prevents potential data loss on deletion under load.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Upstream Percona Operator v1.21.1 ships both delete-psmdb-pvc and
delete-psmdb-pods-in-order in deploy/cr.yaml by default. The
pods-in-order finalizer ensures primary-last graceful shutdown,
preventing data loss on cluster deletion under load.
Currently fails — establishes red phase before adding the finalizer.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Replace the equal assertion (which would block adding more finalizers)
with a sharded-mode test case ensuring the finalizer is rendered
regardless of replicaset/sharded configuration.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Document that uninstalling the release reclaims PVCs and
operator-managed user secrets via the percona.com/delete-psmdb-pvc
finalizer. Pre-existing users who relied on orphaned PVCs to
recover data should take backups before uninstalling.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Rename test description to accurately reflect what's verified
(template renders the finalizer, not runtime cleanup behavior).
Add equal assertion locking the exact finalizer list to catch
regressions where a stray finalizer is appended.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Add helm-unittest assertion verifying that the rendered
PerconaServerMongoDB CR carries percona.com/delete-psmdb-pvc finalizer.
Currently fails — establishes red phase before adding the fix.
Signed-off-by: Arsolitt <arsolitt@gmail.com>
A single LoadBalancer selecting all replset members round-robined writes
across primary and secondaries, so ~⅔ of writes failed with "not primary".
Drivers couldn't auto-recover either: `isMaster` returned in-cluster DNS
names that don't resolve from outside.
Switch to the Percona operator's native `replsets[].expose` so it creates
one LoadBalancer per pod and rewrites `rs.conf` with the external IPs;
the driver then discovers the primary correctly via the standard replica
set connection string. The manual `external-svc.yaml` is now scoped to
sharded mode (mongos front-end) only.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Replace the old MongoDB logo with the official one featuring
the MongoDB leaf icon on a green radial gradient background.
Co-authored-by: Viktoriia Kvapil <159528100+kvapsova@users.noreply.github.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Add migration script to convert existing MongoDB HelmReleases
from old users format to new users + databases format.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Align MongoDB configuration with postgres and mysql patterns:
- Add separate `databases` section with `roles.admin` and `roles.readonly`
- Simplify `users` to only contain optional password field
- All users now authenticate via `admin` database (MongoDB best practice)
- Admin role maps to readWrite + dbAdmin permissions
- Readonly role maps to read permission
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Remove the ability for users to specify custom container images
(images.pmm and images.backup) in the MongoDB application values.
This is a security hardening measure - allowing users to specify
arbitrary container images could lead to running malicious or
compromised images, supply chain attacks, or privilege escalation.
The images are now hardcoded in the template:
- percona/pmm-client:2.44.1
- percona/percona-backup-mongodb:2.11.0
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Add MongoDB managed service based on Percona Operator for MongoDB with:
- Replica set mode (default) and sharded cluster mode
- Configurable replicas, storage, and resource presets
- Custom users with role-based access control
- S3-compatible backup with PITR support
- Bootstrap/restore from backup
- External access support
- WorkloadMonitor integration for dashboard
- Comprehensive helm-unittest test coverage (91 tests)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>