mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-09 22:13:18 +09:00
Two review findings: - The enablement note claimed the two-phase `transition` flow avoids the count dip when enabling autoscaling on an existing cluster. It does not: helm-controller upgrades with Helm's three-way merge (not SSA), which keys off the old-vs-new manifest diff, not KEDA's live /scale value. At phase 2 the chart stops rendering the staged static count, so the field is deleted (no quorum -> CNPG defaults to 1, a brief collapse) or changed (quorum -> rebased to effectiveMin), then the warm KEDA restores it. Rewrite the docs to state the flow reduces (KEDA is pre-warmed, no cold-start) but does not eliminate the dip, and spell out the suspend + scale-subresource pin that actually makes the handoff dip-free. This is the SSA-vs-three-way-merge handoff the design proposal left for the PoC to settle. - Pin metricType: AverageValue on the Prometheus trigger in cozy-lib.keda.scaledObject. The desired = ceil(query / threshold) identity holds only for an AverageValue external metric; it was relying on KEDA's default. Make it explicit and assert it so a re-vendor or default change cannot silently break the arithmetic. Signed-off-by: Alexey Artamonov <aleksei.artamonov@aenix.io>
443 lines
17 KiB
JSON
443 lines
17 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
|
|
}
|
|
}
|
|
},
|
|
"autoscaling": {
|
|
"description": "Read-replica autoscaling configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"required": [
|
|
"dryRun",
|
|
"enabled",
|
|
"maxReplicas",
|
|
"maxReplicationLagSeconds",
|
|
"metric",
|
|
"minReplicas",
|
|
"target",
|
|
"transition"
|
|
],
|
|
"properties": {
|
|
"dryRun": {
|
|
"description": "Recommendation mode: keep the static count and render the ScaledObject paused (KEDA does not actuate). Permanent, unlike `transition`.",
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"enabled": {
|
|
"description": "Enable horizontal autoscaling of read replicas. Requires the platform `keda` package to be enabled by an administrator first; the chart fails to render (rather than break the release) if KEDA is not installed.",
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"maxReplicas": {
|
|
"description": "Maximum total instances; raised to the quorum floor if the floor exceeds it.",
|
|
"type": "integer",
|
|
"default": 6
|
|
},
|
|
"maxReplicationLagSeconds": {
|
|
"description": "Freeze scaling while replication lag exceeds this and the primary is writing.",
|
|
"type": "integer",
|
|
"default": 30
|
|
},
|
|
"metric": {
|
|
"description": "Read-load signal to scale on.",
|
|
"type": "string",
|
|
"default": "ReadConnections",
|
|
"enum": [
|
|
"ReadConnections",
|
|
"ReadCPUUtilization"
|
|
]
|
|
},
|
|
"minReplicas": {
|
|
"description": "Minimum total instances; raised to the synchronous-quorum floor (`maxSyncReplicas + 1`) and to 2 when either is higher.",
|
|
"type": "integer",
|
|
"default": 2
|
|
},
|
|
"target": {
|
|
"description": "Target read load per read-serving replica (unit depends on `metric`: active connections, or CPU millicores). Dimensionless integer - a resource.Quantity here would accept `150m`, which PromQL reads as 150 minutes and KEDA cannot use as a threshold.",
|
|
"type": "integer",
|
|
"default": 150
|
|
},
|
|
"transition": {
|
|
"description": "Migration phase 1: render `.spec.instances` from `replicas` (which the operator stages to the current live count) AND pause the ScaledObject, so KEDA stands up ready-but-not-actuating. Flip to false (phase 2) to hand the runtime count to KEDA. This shortens the handoff (KEDA is already warm, no cold-start) but does not make it atomic — see the enablement note for the residual dip and how to eliminate it.",
|
|
"type": "boolean",
|
|
"default": false
|
|
}
|
|
}
|
|
},
|
|
"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": ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|