mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-30 00:27:04 +09:00
fix(mongodb): keep the cleanup escape hatch off legacy operator CRs, and surface a stuck finalizer
The skip-artifact-cleanup annotation reaches releaseMongoDBCleanup before the ownership check, and the operator-CR delete sat outside the finalizer gate, so a legacy backup (no delete-backup finalizer) lost its PerconaServerMongoDBBackup record when a tenant set the annotation to release a stuck Backup or applied it across a namespace during teardown. That record is what a psmdb restore-by-name resolves against, and both the RBAC comment and the docs promise it is never touched. Gate every write in the release on the finalizer being present: an unowned (legacy) CR or one that cannot be read at all is left intact, and the Cozystack Backup still releases. Surface a failed finalizer strip. The release must proceed even against an unreachable CR, but the swallowed patch error left the operator CR holding percona.com/delete-backup — pinning its namespace in Terminating, the outcome the branch exists to prevent — while the Event reported the success case. Emit a FinalizerNotStripped Warning naming the object so an operator can find it. Mark the sibling backup fields (schedule, retentionPolicy, destinationPath, endpointURL, insecureSkipTLSVerify, s3AccessKey, s3SecretKey) as ignored under useSystemBucket, so a tenant editing one instead of the flag itself is not misled by a description that describes only the legacy flow; regenerate the schema, README and mongodb-rd. Pair the insecureSkipTLSVerify rendering assertion with a case that flips the platform knob on, so the two together discriminate the rendered value from a hardcoded default. Assisted-By: LLM Signed-off-by: Andrey Kolkov <androndo@gmail.com>
This commit is contained in:
1 parent
d58322e253
commit
cc51e040ae
8 files changed
+141
-50
No files matched your search
Generated
+7
-7
@@ -60,28 +60,28 @@ type ConfigSpec struct {
|
||||
}
|
||||
|
||||
type Backup struct {
|
||||
// Destination path for backups (e.g. s3://bucket/path/).
|
||||
// Destination path for backups (e.g. s3://bucket/path/). Ignored when useSystemBucket is true (the driver injects the platform bucket).
|
||||
// +kubebuilder:default:="s3://bucket/path/to/folder/"
|
||||
DestinationPath string `json:"destinationPath,omitempty"`
|
||||
// Enable regular backups.
|
||||
// +kubebuilder:default:=false
|
||||
Enabled bool `json:"enabled"`
|
||||
// S3 endpoint URL for uploads.
|
||||
// S3 endpoint URL for uploads. Ignored when useSystemBucket is true (the strategy supplies the platform endpoint).
|
||||
// +kubebuilder:default:="http://minio-gateway-service:9000"
|
||||
EndpointURL string `json:"endpointURL,omitempty"`
|
||||
// Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs).
|
||||
// Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs). Ignored when useSystemBucket is true (the platform backupStorage knob applies instead).
|
||||
// +kubebuilder:default:=false
|
||||
InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"`
|
||||
// Retention policy (e.g. "30d").
|
||||
// Retention policy (e.g. "30d"). Ignored when useSystemBucket is true (no engine-side retention on that flow).
|
||||
// +kubebuilder:default:="30d"
|
||||
RetentionPolicy string `json:"retentionPolicy,omitempty"`
|
||||
// Access key for S3 authentication.
|
||||
// Access key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds).
|
||||
// +kubebuilder:default:=""
|
||||
S3AccessKey string `json:"s3AccessKey,omitempty"`
|
||||
// Secret key for S3 authentication.
|
||||
// Secret key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds).
|
||||
// +kubebuilder:default:=""
|
||||
S3SecretKey string `json:"s3SecretKey,omitempty"`
|
||||
// Cron schedule for automated backups.
|
||||
// Cron schedule for automated backups. Ignored when useSystemBucket is true (scheduling moves to the BackupClass Plan).
|
||||
// +kubebuilder:default:="0 2 * * *"
|
||||
Schedule string `json:"schedule,omitempty"`
|
||||
// Opt-in: back up to the platform system bucket without supplying S3 credentials. The cozy-default MongoDB BackupClass injects the storage at BackupJob time; tenants do not set s3AccessKey/s3SecretKey/destinationPath/endpointURL, and backups scope to `<namespace>/<application>`. Scheduling and retention move to the BackupClass Plan, so `schedule`, `retentionPolicy` and PITR are ignored while this is on. See docs/operations/backup-classes.md.
|
||||
|
||||
@@ -792,28 +792,49 @@ func (r *BackupReconciler) cleanupMongoDBBackup(ctx context.Context, backup *bac
|
||||
return ctrl.Result{RequeueAfter: psmdbPollInterval}, nil
|
||||
}
|
||||
|
||||
// releaseMongoDBCleanup gives up pruning the archive and lets the Cozystack
|
||||
// Backup (and any enclosing namespace) finish deleting: best-effort, it strips
|
||||
// the driver's delete-backup finalizer from the operator CR so the CR no longer
|
||||
// blocks teardown and deletes that CR, then records a Warning naming the object
|
||||
// left in the bucket. Every apiserver step is best-effort — a failed read or
|
||||
// write is swallowed — because the caller reaches here precisely to let a wedged
|
||||
// Backup go, and must never be blocked by the same unreachable CR. Takes the CR
|
||||
// name rather than a fetched object so it works on the annotation path, which
|
||||
// runs before any read. Mirrors the Redis/Rabbitmq release path.
|
||||
// releaseMongoDBCleanup lets the Cozystack Backup (and any enclosing namespace)
|
||||
// finish deleting when the archive cannot be pruned in place. It only ever
|
||||
// touches an operator CR the driver OWNS — one carrying the delete-backup
|
||||
// finalizer: it strips that finalizer so the CR no longer blocks teardown and
|
||||
// deletes it. A legacy CR (no finalizer) or one that cannot be read at all is
|
||||
// left entirely intact, so the never-deleted contract for legacy backups holds
|
||||
// even though the annotation reaches this path before the ownership check, and
|
||||
// an unreadable CR still releases. The finalizer strip is best-effort — the
|
||||
// release must proceed even against an unreachable CR — but a failed strip is
|
||||
// surfaced (log + Event), because the CR then keeps the finalizer and can hold
|
||||
// its namespace in Terminating, and the operator needs to know which one. Takes
|
||||
// the CR name so it works on the annotation path, which runs before any read.
|
||||
func (r *BackupReconciler) releaseMongoDBCleanup(ctx context.Context, backup *backupsv1alpha1.Backup, sourceBackupName, reason string) (ctrl.Result, error) {
|
||||
logger := getLogger(ctx)
|
||||
live := &psmdbtypes.PerconaServerMongoDBBackup{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Namespace: backup.Namespace, Name: sourceBackupName}, live); err == nil {
|
||||
if controllerutil.ContainsFinalizer(live, psmdbDeleteBackupFinalizer) {
|
||||
base := live.DeepCopy()
|
||||
controllerutil.RemoveFinalizer(live, psmdbDeleteBackupFinalizer)
|
||||
_ = r.Patch(ctx, live, client.MergeFrom(base))
|
||||
}
|
||||
if live.DeletionTimestamp.IsZero() {
|
||||
_ = r.Delete(ctx, live)
|
||||
}
|
||||
err := r.Get(ctx, types.NamespacedName{Namespace: backup.Namespace, Name: sourceBackupName}, live)
|
||||
if err != nil || !controllerutil.ContainsFinalizer(live, psmdbDeleteBackupFinalizer) {
|
||||
// Legacy backup (no delete-backup finalizer) or an unreadable CR: nothing
|
||||
// of the driver's to strip or delete. Leave the operator CR untouched and
|
||||
// release the Cozystack Backup.
|
||||
logger.Debug("releasing MongoDB Backup; no driver-owned archive to prune",
|
||||
"backup", backup.Name, "sourceBackup", sourceBackupName, "reason", reason)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
getLogger(ctx).Info("releasing MongoDB Backup without pruning its object",
|
||||
|
||||
base := live.DeepCopy()
|
||||
controllerutil.RemoveFinalizer(live, psmdbDeleteBackupFinalizer)
|
||||
if perr := r.Patch(ctx, live, client.MergeFrom(base)); perr != nil {
|
||||
// The release still proceeds (an unreachable CR must not wedge the
|
||||
// Backup), but the CR keeps the finalizer and can pin its namespace in
|
||||
// Terminating — surface which object so an operator can find it.
|
||||
logger.Info("released MongoDB Backup but could not strip the delete-backup finalizer; the operator CR may hold its namespace in Terminating",
|
||||
"backup", backup.Name, "sourceBackup", sourceBackupName, "reason", reason, "error", perr)
|
||||
if r.Recorder != nil {
|
||||
r.Recorder.Eventf(backup, corev1.EventTypeWarning, "FinalizerNotStripped",
|
||||
"released Backup but could not strip the delete-backup finalizer from PerconaServerMongoDBBackup %s (%v); it may keep the namespace in Terminating", sourceBackupName, perr)
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
if live.DeletionTimestamp.IsZero() {
|
||||
_ = r.Delete(ctx, live)
|
||||
}
|
||||
logger.Info("releasing MongoDB Backup, leaving its shared-bucket object for a lifecycle policy",
|
||||
"backup", backup.Name, "sourceBackup", sourceBackupName, "reason", reason)
|
||||
if r.Recorder != nil {
|
||||
r.Recorder.Eventf(backup, corev1.EventTypeWarning, "ArtifactNotDeleted",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -1507,4 +1508,57 @@ func TestCleanupMongoDBBackup(t *testing.T) {
|
||||
t.Fatalf("NoMatchError must be treated as gone and release, got res=%+v err=%v", res, err)
|
||||
}
|
||||
})
|
||||
|
||||
// The annotation reaches releaseMongoDBCleanup before the ownership check, so
|
||||
// the release must itself refuse to touch a CR the driver does not own — the
|
||||
// legacy operator-side backup record is what a psmdb restore-by-name resolves
|
||||
// against, and the RBAC comment and docs both promise it is never deleted.
|
||||
t.Run("legacy backup with the skip annotation keeps its operator CR", func(t *testing.T) {
|
||||
c := newMongoDBStrategyTestClient(t, opBackup(false), liveNamespace)
|
||||
r := &BackupReconciler{Client: c, Recorder: record.NewFakeRecorder(10)}
|
||||
res, err := r.cleanupMongoDBBackup(context.Background(), newBackup(map[string]string{psmdbSkipArtifactCleanupAnnotation: "true"}))
|
||||
if err != nil || res.RequeueAfter != 0 {
|
||||
t.Fatalf("legacy + annotation must release, got res=%+v err=%v", res, err)
|
||||
}
|
||||
got := getOp(t, c)
|
||||
if !got.DeletionTimestamp.IsZero() {
|
||||
t.Errorf("the escape hatch must not delete a legacy (unowned) operator CR")
|
||||
}
|
||||
})
|
||||
|
||||
// When the finalizer strip fails, the CR keeps percona.com/delete-backup and
|
||||
// can pin its namespace in Terminating; the release still proceeds, but it
|
||||
// must say which object is stuck rather than emit the success-case Event.
|
||||
t.Run("a failed finalizer strip surfaces the stuck object", func(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
_ = scheme.AddToScheme(s)
|
||||
_ = backupsv1alpha1.AddToScheme(s)
|
||||
_ = psmdbtypes.AddToScheme(s)
|
||||
c := clientfake.NewClientBuilder().
|
||||
WithScheme(s).
|
||||
WithObjects(opBackup(true)).
|
||||
WithInterceptorFuncs(interceptor.Funcs{
|
||||
Patch: func(ctx context.Context, cl client.WithWatch, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
if _, ok := obj.(*psmdbtypes.PerconaServerMongoDBBackup); ok {
|
||||
return apierrors.NewServiceUnavailable("apiserver down")
|
||||
}
|
||||
return cl.Patch(ctx, obj, patch, opts...)
|
||||
},
|
||||
}).
|
||||
Build()
|
||||
rec := record.NewFakeRecorder(10)
|
||||
r := &BackupReconciler{Client: c, Recorder: rec}
|
||||
res, err := r.cleanupMongoDBBackup(context.Background(), newBackup(map[string]string{psmdbSkipArtifactCleanupAnnotation: "true"}))
|
||||
if err != nil || res.RequeueAfter != 0 {
|
||||
t.Fatalf("release must proceed even when the strip fails, got res=%+v err=%v", res, err)
|
||||
}
|
||||
select {
|
||||
case ev := <-rec.Events:
|
||||
if !strings.Contains(ev, "FinalizerNotStripped") || !strings.Contains(ev, "op-bk") {
|
||||
t.Errorf("expected a FinalizerNotStripped event naming the object, got %q", ev)
|
||||
}
|
||||
default:
|
||||
t.Errorf("expected an event surfacing the stuck finalizer, got none")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -252,13 +252,13 @@ Releases with `external: false`, and sharded releases in any configuration, are
|
||||
| `backup` | Backup configuration. | `object` | `{}` |
|
||||
| `backup.enabled` | Enable regular backups. | `bool` | `false` |
|
||||
| `backup.useSystemBucket` | Opt-in: back up to the platform system bucket without supplying S3 credentials. The cozy-default MongoDB BackupClass injects the storage at BackupJob time; tenants do not set s3AccessKey/s3SecretKey/destinationPath/endpointURL, and backups scope to `<namespace>/<application>`. Scheduling and retention move to the BackupClass Plan, so `schedule`, `retentionPolicy` and PITR are ignored while this is on. See docs/operations/backup-classes.md. | `bool` | `false` |
|
||||
| `backup.schedule` | Cron schedule for automated backups. | `string` | `0 2 * * *` |
|
||||
| `backup.retentionPolicy` | Retention policy (e.g. "30d"). | `string` | `30d` |
|
||||
| `backup.destinationPath` | Destination path for backups (e.g. s3://bucket/path/). | `string` | `s3://bucket/path/to/folder/` |
|
||||
| `backup.endpointURL` | S3 endpoint URL for uploads. | `string` | `http://minio-gateway-service:9000` |
|
||||
| `backup.insecureSkipTLSVerify` | Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs). | `bool` | `false` |
|
||||
| `backup.s3AccessKey` | Access key for S3 authentication. | `string` | `""` |
|
||||
| `backup.s3SecretKey` | Secret key for S3 authentication. | `string` | `""` |
|
||||
| `backup.schedule` | Cron schedule for automated backups. Ignored when useSystemBucket is true (scheduling moves to the BackupClass Plan). | `string` | `0 2 * * *` |
|
||||
| `backup.retentionPolicy` | Retention policy (e.g. "30d"). Ignored when useSystemBucket is true (no engine-side retention on that flow). | `string` | `30d` |
|
||||
| `backup.destinationPath` | Destination path for backups (e.g. s3://bucket/path/). Ignored when useSystemBucket is true (the driver injects the platform bucket). | `string` | `s3://bucket/path/to/folder/` |
|
||||
| `backup.endpointURL` | S3 endpoint URL for uploads. Ignored when useSystemBucket is true (the strategy supplies the platform endpoint). | `string` | `http://minio-gateway-service:9000` |
|
||||
| `backup.insecureSkipTLSVerify` | Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs). Ignored when useSystemBucket is true (the platform backupStorage knob applies instead). | `bool` | `false` |
|
||||
| `backup.s3AccessKey` | Access key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds). | `string` | `""` |
|
||||
| `backup.s3SecretKey` | Secret key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds). | `string` | `""` |
|
||||
|
||||
|
||||
### Bootstrap (recovery) parameters
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
],
|
||||
"properties": {
|
||||
"destinationPath": {
|
||||
"description": "Destination path for backups (e.g. s3://bucket/path/).",
|
||||
"description": "Destination path for backups (e.g. s3://bucket/path/). Ignored when useSystemBucket is true (the driver injects the platform bucket).",
|
||||
"type": "string",
|
||||
"default": "s3://bucket/path/to/folder/"
|
||||
},
|
||||
@@ -283,32 +283,32 @@
|
||||
"default": false
|
||||
},
|
||||
"endpointURL": {
|
||||
"description": "S3 endpoint URL for uploads.",
|
||||
"description": "S3 endpoint URL for uploads. Ignored when useSystemBucket is true (the strategy supplies the platform endpoint).",
|
||||
"type": "string",
|
||||
"default": "http://minio-gateway-service:9000"
|
||||
},
|
||||
"insecureSkipTLSVerify": {
|
||||
"description": "Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs).",
|
||||
"description": "Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs). Ignored when useSystemBucket is true (the platform backupStorage knob applies instead).",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"retentionPolicy": {
|
||||
"description": "Retention policy (e.g. \"30d\").",
|
||||
"description": "Retention policy (e.g. \"30d\"). Ignored when useSystemBucket is true (no engine-side retention on that flow).",
|
||||
"type": "string",
|
||||
"default": "30d"
|
||||
},
|
||||
"s3AccessKey": {
|
||||
"description": "Access key for S3 authentication.",
|
||||
"description": "Access key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds).",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"s3SecretKey": {
|
||||
"description": "Secret key for S3 authentication.",
|
||||
"description": "Secret key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds).",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"schedule": {
|
||||
"description": "Cron schedule for automated backups.",
|
||||
"description": "Cron schedule for automated backups. Ignored when useSystemBucket is true (scheduling moves to the BackupClass Plan).",
|
||||
"type": "string",
|
||||
"default": "0 2 * * *"
|
||||
},
|
||||
|
||||
@@ -156,13 +156,13 @@ databases: {}
|
||||
## @typedef {struct} Backup - Backup configuration.
|
||||
## @field {bool} enabled - Enable regular backups.
|
||||
## @field {bool} [useSystemBucket] - Opt-in: back up to the platform system bucket without supplying S3 credentials. The cozy-default MongoDB BackupClass injects the storage at BackupJob time; tenants do not set s3AccessKey/s3SecretKey/destinationPath/endpointURL, and backups scope to `<namespace>/<application>`. Scheduling and retention move to the BackupClass Plan, so `schedule`, `retentionPolicy` and PITR are ignored while this is on. See docs/operations/backup-classes.md.
|
||||
## @field {string} [schedule] - Cron schedule for automated backups.
|
||||
## @field {string} [retentionPolicy] - Retention policy (e.g. "30d").
|
||||
## @field {string} [destinationPath] - Destination path for backups (e.g. s3://bucket/path/).
|
||||
## @field {string} [endpointURL] - S3 endpoint URL for uploads.
|
||||
## @field {bool} [insecureSkipTLSVerify] - Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs).
|
||||
## @field {string} [s3AccessKey] - Access key for S3 authentication.
|
||||
## @field {string} [s3SecretKey] - Secret key for S3 authentication.
|
||||
## @field {string} [schedule] - Cron schedule for automated backups. Ignored when useSystemBucket is true (scheduling moves to the BackupClass Plan).
|
||||
## @field {string} [retentionPolicy] - Retention policy (e.g. "30d"). Ignored when useSystemBucket is true (no engine-side retention on that flow).
|
||||
## @field {string} [destinationPath] - Destination path for backups (e.g. s3://bucket/path/). Ignored when useSystemBucket is true (the driver injects the platform bucket).
|
||||
## @field {string} [endpointURL] - S3 endpoint URL for uploads. Ignored when useSystemBucket is true (the strategy supplies the platform endpoint).
|
||||
## @field {bool} [insecureSkipTLSVerify] - Skip TLS verification for the S3 endpoint (self-signed, e.g. in-cluster seaweedfs). Ignored when useSystemBucket is true (the platform backupStorage knob applies instead).
|
||||
## @field {string} [s3AccessKey] - Access key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds).
|
||||
## @field {string} [s3SecretKey] - Secret key for S3 authentication. Ignored when useSystemBucket is true (credentials come from the projected cozy-backups-creds).
|
||||
|
||||
## @param {Backup} backup - Backup configuration.
|
||||
backup:
|
||||
|
||||
@@ -179,8 +179,10 @@ tests:
|
||||
value: true
|
||||
template: templates/strategy-mongodb-default.yaml
|
||||
# insecureSkipTLSVerify is plumbed end-to-end (strategy -> driver-injected
|
||||
# storage entry); the template must render it from the platform knob, not
|
||||
# leave it unset (which pins the injected entry to false regardless).
|
||||
# storage entry). This case pins the default (knob off -> false); the
|
||||
# "renders insecureSkipTLSVerify from the platform knob" case below flips
|
||||
# the knob on, so the pair discriminates the rendered value from a
|
||||
# hardcoded literal (either would redden one of the two).
|
||||
- equal:
|
||||
path: spec.template.s3.insecureSkipTLSVerify
|
||||
value: false
|
||||
@@ -240,6 +242,20 @@ tests:
|
||||
value: "true"
|
||||
template: templates/velero-bsl.yaml
|
||||
|
||||
- it: "the MongoDB strategy renders insecureSkipTLSVerify from the platform knob"
|
||||
# Pairs with the default (false) assertion above: with the knob flipped on,
|
||||
# the injected storage entry must carry true. A template that hardcoded either
|
||||
# literal would redden one of the two cases, so together they prove the value
|
||||
# tracks backupStorage.insecureSkipTLSVerify rather than a constant.
|
||||
set:
|
||||
backupStorage.bucketNameOverride: test-bucket
|
||||
backupStorage.insecureSkipTLSVerify: true
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.s3.insecureSkipTLSVerify
|
||||
value: true
|
||||
template: templates/strategy-mongodb-default.yaml
|
||||
|
||||
- it: "external S3 (provisionBucket=false): Strategy CRs render against the configured bucketName, no Bucket CR is created"
|
||||
set:
|
||||
backupStorage.provisionBucket: false
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ spec:
|
||||
singular: mongodb
|
||||
plural: mongodbs
|
||||
openAPISchema: |-
|
||||
{"title":"Chart Values","type":"object","properties":{"replicas":{"description":"Number of MongoDB replicas in replica set.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"t1.small","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"","x-kubernetes-validations":[{"rule":"self == oldSelf","message":"storageClass is immutable"}],"x-cozystack-options":{"source":"storageclass"}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"version":{"description":"MongoDB major version to deploy.","type":"string","default":"v8","enum":["v8","v7","v6"]},"sharding":{"description":"Enable sharded cluster mode. When disabled, deploys a replica set.","type":"boolean","default":false},"shardingConfig":{"description":"Configuration for sharded cluster mode.","type":"object","default":{},"required":["configServerSize","configServers","mongos"],"properties":{"configServerSize":{"description":"PVC size for config servers.","default":"3Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"configServers":{"description":"Number of config server replicas.","type":"integer","default":3},"mongos":{"description":"Number of mongos router replicas.","type":"integer","default":2},"shards":{"description":"List of shard configurations.","type":"array","default":[{"name":"rs0","replicas":3,"size":"10Gi"}],"items":{"type":"object","required":["name","replicas","size"],"properties":{"name":{"description":"Shard name.","type":"string"},"replicas":{"description":"Number of replicas in this shard.","type":"integer"},"size":{"description":"PVC size for this shard.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}}},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"}}}},"databases":{"description":"Databases configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"roles":{"description":"Roles assigned to users.","type":"object","properties":{"admin":{"description":"List of users with admin privileges (readWrite + dbAdmin).","type":"array","items":{"type":"string"}},"readonly":{"description":"List of users with read-only privileges.","type":"array","items":{"type":"string"}}}}}}},"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"insecureSkipTLSVerify":{"description":"Skip TLS verification for the S3 endpoint (self-Line truncated
|
||||
{"title":"Chart Values","type":"object","properties":{"replicas":{"description":"Number of MongoDB replicas in replica set.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"t1.small","enum":["t1.nano","t1.micro","t1.small","t1.medium","t1.large","t1.xlarge","t1.2xlarge","t1.4xlarge","c1.nano","c1.micro","c1.small","c1.medium","c1.large","c1.xlarge","c1.2xlarge","c1.4xlarge","s1.nano","s1.micro","s1.small","s1.medium","s1.large","s1.xlarge","s1.2xlarge","s1.4xlarge","u1.nano","u1.micro","u1.small","u1.medium","u1.large","u1.xlarge","u1.2xlarge","u1.4xlarge","m1.nano","m1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m1.2xlarge","m1.4xlarge","nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"","x-kubernetes-validations":[{"rule":"self == oldSelf","message":"storageClass is immutable"}],"x-cozystack-options":{"source":"storageclass"}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"version":{"description":"MongoDB major version to deploy.","type":"string","default":"v8","enum":["v8","v7","v6"]},"sharding":{"description":"Enable sharded cluster mode. When disabled, deploys a replica set.","type":"boolean","default":false},"shardingConfig":{"description":"Configuration for sharded cluster mode.","type":"object","default":{},"required":["configServerSize","configServers","mongos"],"properties":{"configServerSize":{"description":"PVC size for config servers.","default":"3Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"configServers":{"description":"Number of config server replicas.","type":"integer","default":3},"mongos":{"description":"Number of mongos router replicas.","type":"integer","default":2},"shards":{"description":"List of shard configurations.","type":"array","default":[{"name":"rs0","replicas":3,"size":"10Gi"}],"items":{"type":"object","required":["name","replicas","size"],"properties":{"name":{"description":"Shard name.","type":"string"},"replicas":{"description":"Number of replicas in this shard.","type":"integer"},"size":{"description":"PVC size for this shard.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}}},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"}}}},"databases":{"description":"Databases configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"roles":{"description":"Roles assigned to users.","type":"object","properties":{"admin":{"description":"List of users with admin privileges (readWrite + dbAdmin).","type":"array","items":{"type":"string"}},"readonly":{"description":"List of users with read-only privileges.","type":"array","items":{"type":"string"}}}}}}},"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/). Ignored when useSystemBucket is true (the driver injects the platform bucket).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads. Ignored when useSystemBucket is true (the strategy supplies the platform Line truncated
|
||||
release:
|
||||
prefix: mongodb-
|
||||
labels:
|
||||
|
||||
Reference in new issue
Block a user