mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-03 10:37:21 +09:00
An in-place RestoreJob (targetApplicationRef omitted) re-bootstraps the
Postgres app under its own name. On stock v1.6.2 it never comes back up,
for two independent reasons in the same restore patch - both surface only
in place, because a to-copy restore targets a different app.
1. Archive-vs-recovery serverName collision. The driver set the restored
cluster's WAL-archive serverName equal to the recovery source
(bootstrap.serverName = bootstrap.oldName = sourceServerName) while the
chart pinned the archiver serverName to the release name. In place those
coincide, so the cluster archived onto the source's own WAL prefix and
barman-cloud-check-wal-archive refused to start ("Expected empty
archive"). Fixed by giving the restored cluster a fresh
bootstrap.newServerName, keyed by the RestoreJob UID (unique per
restore, stable across reconciles); the chart archives there while
externalClusters still recovers from bootstrap.serverName. This is what
the manual CNPG strategies hit (serverName defaults to the cluster
name); the cozy-default flow dodged it (serverName is <ns>-<app>).
2. useSystemBucket guard. The restore populates explicit S3 coordinates
(destinationPath / s3CredentialsSecret / endpointCA) but left
backup.useSystemBucket=true, so the chart's "bootstrap.enabled is
incompatible with useSystemBucket" guard failed the HelmRelease upgrade
and the cluster never re-rendered. This is what the cozy-default
(system-bucket) flow hits on an in-place restore. Fixed by clearing
useSystemBucket in the restore patch, since the app is now on the
explicit-creds path. The driver's app view (postgresapp.Backup) gains
the field, without omitempty so a false survives the merge patch.
Both are exercised only by an in-place restore; the postgres backup e2e
drove only to-copy, so neither surfaced in CI. A step 50 in-place restore
now guards reason 1 (manual-strategy flow); reason 2 is covered by a Go
unit assertion (a system-bucket in-place e2e needs the platform bucket in
CI and is left as a follow-up).
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 5bcdb8cfb8)
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: LLM
382 lines
15 KiB
JSON
382 lines
15 KiB
JSON
{
|
|
"title": "Chart Values",
|
|
"type": "object",
|
|
"properties": {
|
|
"replicas": {
|
|
"description": "Number of Postgres replicas.",
|
|
"type": "integer",
|
|
"default": 2
|
|
},
|
|
"resources": {
|
|
"description": "Explicit CPU and memory configuration for each PostgreSQL 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.micro",
|
|
"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": "PostgreSQL major version to deploy",
|
|
"type": "string",
|
|
"default": "v18",
|
|
"enum": [
|
|
"v18",
|
|
"v17",
|
|
"v16",
|
|
"v15",
|
|
"v14",
|
|
"v13"
|
|
]
|
|
},
|
|
"tls": {
|
|
"description": "TLS configuration for server connections.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"enabled": {
|
|
"description": "Tri-state switch controlling whether the chart injects the external hostname into the operator-managed CNPG cert via spec.certificates.serverAltDNSNames. When omitted, the chart injects the SAN if `external: true` and skips it otherwise. Set explicitly to `true` to inject regardless of `external` (no-op when `external: false` since there is no external hostname to add). Set to `false` to skip injection. Note that CNPG keeps its built-in TLS on the wire regardless of this flag — this toggle only controls the chart-side SAN injection; to disable PostgreSQL TLS entirely set `postgresql.parameters.ssl = \"off\"` at the CNPG layer.",
|
|
"type": "boolean"
|
|
}
|
|
}
|
|
},
|
|
"postgresql": {
|
|
"description": "PostgreSQL server configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"parameters": {
|
|
"description": "PostgreSQL server parameters. Values may be strings or integers; integers are coerced to strings by the template (e.g. both `max_connections: 100` and `max_connections: \"100\"` are accepted). BLOCKED (enable arbitrary code execution): archive_command, restore_command, ssl_passphrase_command, archive_cleanup_command, recovery_end_command, dynamic_library_path, local_preload_libraries, session_preload_libraries, shared_preload_libraries. Do NOT override CloudNativePG-managed parameters: archive_mode, primary_conninfo, wal_level, max_replication_slots.",
|
|
"type": "object",
|
|
"default": {
|
|
"max_connections": "100"
|
|
},
|
|
"additionalProperties": {
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"quorum": {
|
|
"description": "Quorum configuration for synchronous replication.",
|
|
"type": "object",
|
|
"default": {},
|
|
"required": [
|
|
"maxSyncReplicas",
|
|
"minSyncReplicas"
|
|
],
|
|
"properties": {
|
|
"maxSyncReplicas": {
|
|
"description": "Maximum number of synchronous replicas allowed (must be less than total replicas).",
|
|
"type": "integer",
|
|
"default": 0
|
|
},
|
|
"minSyncReplicas": {
|
|
"description": "Minimum number of synchronous replicas required for commit.",
|
|
"type": "integer",
|
|
"default": 0
|
|
}
|
|
}
|
|
},
|
|
"users": {
|
|
"description": "Users configuration map.",
|
|
"type": "object",
|
|
"default": {},
|
|
"additionalProperties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"password": {
|
|
"description": "Password for the user.",
|
|
"type": "string"
|
|
},
|
|
"replication": {
|
|
"description": "Whether the user has replication privileges.",
|
|
"type": "boolean"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"databases": {
|
|
"description": "Databases configuration map.",
|
|
"type": "object",
|
|
"default": {},
|
|
"additionalProperties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"extensions": {
|
|
"description": "List of enabled PostgreSQL extensions.",
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"roles": {
|
|
"description": "Roles assigned to users.",
|
|
"type": "object",
|
|
"properties": {
|
|
"admin": {
|
|
"description": "List of users with admin privileges.",
|
|
"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": "DEPRECATED. Per-tenant S3 configuration is superseded by the platform-managed `cozy-default` BackupClass and the `cozy-backups` system bucket. Leave empty for new installations; the BackupClass driver picks up the system-managed coordinates. Kept for in-place upgrade compatibility.",
|
|
"type": "string",
|
|
"default": "s3://bucket/path/to/folder/"
|
|
},
|
|
"enabled": {
|
|
"description": "Enable regular backups.",
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"endpointCA": {
|
|
"description": "DEPRECATED. Pre-existing Secret with the CA bundle the barman-cloud plugin should trust when reaching a self-signed S3 endpoint. Used for both backup and bootstrap recovery in the legacy chart-managed flow.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"key": {
|
|
"description": "Key within the Secret containing the CA bundle. Defaults to `ca.crt`.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"name": {
|
|
"description": "Name of the Secret in the application namespace. Empty means no endpointCA is emitted (the plugin uses the system trust store).",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
},
|
|
"endpointURL": {
|
|
"description": "DEPRECATED. See `destinationPath`.",
|
|
"type": "string",
|
|
"default": "http://minio-gateway-service:9000"
|
|
},
|
|
"retentionPolicy": {
|
|
"description": "Retention policy (e.g. \"30d\").",
|
|
"type": "string",
|
|
"default": "30d"
|
|
},
|
|
"s3AccessKey": {
|
|
"description": "DEPRECATED. Tenants no longer supply S3 keys; the system Bucket Secret is projected into the tenant namespace by the backup controller. Ignored when `s3CredentialsSecret.name` is set or `useSystemBucket` is true. The chart skips materialising `\u003crelease\u003e-s3-creds` whenever this field is empty so a default install does not leak placeholder credentials into the tenant namespace.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"s3CredentialsSecret": {
|
|
"description": "DEPRECATED. Pre-existing Secret with S3 credentials. Use the platform-managed `cozy-default` BackupClass instead. When set, the chart references this Secret directly (legacy chart-managed flow). The CNPG backup driver writes this field on restore so credentials never land in the CR `.spec`.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"accessKeyIDKey": {
|
|
"description": "Key in the Secret holding the access key ID. Defaults to `AWS_ACCESS_KEY_ID`.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"name": {
|
|
"description": "Name of the Secret in the application namespace. Empty means the chart materialises `\u003crelease\u003e-s3-creds` from `s3AccessKey`/`s3SecretKey`.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"secretAccessKeyKey": {
|
|
"description": "Key in the Secret holding the secret access key. Defaults to `AWS_SECRET_ACCESS_KEY`.",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
},
|
|
"s3SecretKey": {
|
|
"description": "DEPRECATED. See `s3AccessKey`.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"schedule": {
|
|
"description": "Legacy. Cron schedule (CNPG 6-field format) for the chart-emitted ScheduledBackup. Empty means no chart-managed schedule, which is the recommended setup when a `BackupClass` from `backups.cozystack.io` already drives backup orchestration. In the legacy chart-managed flow `spec.plugins` plus the barman-cloud ObjectStore is rendered when `backup.enabled=true` AND `useSystemBucket=false` AND `destinationPath` is non-empty AND inline-or-external creds are supplied; in the platform `useSystemBucket=true` flow the chart skips emitting `spec.plugins` and the CNPG driver SSA-applies the ObjectStore and patches `spec.plugins` onto the live Cluster at first BackupJob time.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"useSystemBucket": {
|
|
"description": "Opt-in: when true, the chart-emitted `\u003crelease\u003e-s3-creds` Secret is skipped AND `spec.plugins` (plus the barman-cloud ObjectStore) is left UNSET in the chart-rendered Cluster — the cozy-default BackupClass driver SSA-applies an ObjectStore (carrying destinationPath/endpointURL/credentials) and patches `spec.plugins` on the live Cluster when the first BackupJob runs. Consequence: plugin WAL archiving is NOT active until that first BackupJob fires; WAL accumulates on the PVC in the meantime, so fire an ad-hoc BackupJob immediately after enabling the flag on existing releases. Use together with the platform `cozy-default` BackupClass — tenants do not need to fill `s3AccessKey`/`s3SecretKey` or `destinationPath`/`endpointURL`. The destination path automatically scopes to `s3://cozy-backups/\u003cnamespace\u003e/\u003crelease\u003e/`.",
|
|
"type": "boolean",
|
|
"default": false
|
|
}
|
|
}
|
|
},
|
|
"bootstrap": {
|
|
"description": "Bootstrap configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"required": [
|
|
"enabled",
|
|
"oldName"
|
|
],
|
|
"properties": {
|
|
"enabled": {
|
|
"description": "Whether to restore from a backup.",
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"newServerName": {
|
|
"description": "WAL-archive server name (S3 path prefix) the RESTORED cluster writes to. Must differ from `serverName` (the recovery source) so a restored cluster archives to a fresh, empty prefix and barman-cloud-check-wal-archive passes while it recovers from the source's prefix. The CNPG backup driver sets this on restore; empty means the cluster archives under its own name.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"oldName": {
|
|
"description": "Previous cluster name before deletion.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"recoveryTime": {
|
|
"description": "Timestamp (RFC3339) for point-in-time recovery; empty means latest.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"serverName": {
|
|
"description": "Server name (S3 path prefix) used by the original cluster when writing backups; passed to the barman-cloud plugin via `externalClusters[].plugin.parameters.serverName`. Defaults to `bootstrap.oldName`. Set this only when the original cluster wrote backups under an explicit server name that differed from its Kubernetes resource name.",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|